@developpement/tp-chatbot-widget 0.0.4 → 0.0.6

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.4",
3
+ "version": "0.0.6",
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: '#7b1fa2', 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,7 +474,7 @@
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
480
  this.addMessage('assistant', `Bonjour ${this.user_info?.first_name || ''} ! Comment puis-je vous aider ?`);
@@ -518,7 +539,7 @@
518
539
 
519
540
  const form_el = this.querySelector('#tp-guest-form');
520
541
  if (form_el) form_el.remove();
521
-
542
+ this.view = 'chat';
522
543
  this.conversation_id = await this.createConversation();
523
544
 
524
545
  const input_bar = this.querySelector('#tp-chatbot-input-bar');
@@ -583,7 +604,7 @@
583
604
  if (typing) typing.remove();
584
605
  }
585
606
 
586
- showClosed() {
607
+ showClosed(existing_csat = null) {
587
608
  this.is_closed = true;
588
609
  if (this.polling_interval) {
589
610
  clearInterval(this.polling_interval);
@@ -603,16 +624,30 @@
603
624
  if (close_bar) close_bar.style.display = 'none';
604
625
 
605
626
  const container = this.querySelector('#tp-messages');
606
- if (this.querySelector('#tp-closed-banner')) return;
627
+
628
+ const existing_banner = this.querySelector('#tp-closed-banner');
629
+ if (existing_banner) {
630
+ if (existing_banner.querySelector('#tp-csat-block')) return; // déjà complet
631
+ existing_banner.remove(); // incomplet, on recrée
632
+ }
607
633
 
608
634
  const color = this.getThemeColor();
609
635
  const dark = this.shadeColor(color, -20);
610
636
 
637
+ const csat_html = existing_csat
638
+ ? `<div style="font-size:13px;color:#888;margin-bottom:14px;">${existing_csat === 'positive' ? '👍 Merci pour votre retour !' : '👎 Merci, nous allons nous améliorer.'}</div>`
639
+ : `<div style="font-size:12px;color:#888;margin-bottom:10px;">Cette conversation vous a-t-elle été utile ?</div>
640
+ <div style="display:flex;gap:10px;justify-content:center;margin-bottom:14px;">
641
+ <button id="tp-csat-positive" style="padding:8px 20px;border-radius:8px;border:1.5px solid #22c55e;background:white;color:#22c55e;font-size:18px;cursor:pointer;font-weight:700;">👍</button>
642
+ <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>
643
+ </div>`;
644
+
611
645
  const banner = document.createElement('div');
612
646
  banner.id = 'tp-closed-banner';
613
647
  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>
648
+ <div style="margin:16px;padding:16px;background:#f9f5ff;border:1px solid #ede8f5;border-radius:12px;text-align:center;">
649
+ <div style="font-size:13px;color:#1a1a2e;font-weight:600;margin-bottom:8px;">✅ Conversation clôturée</div>
650
+ <div id="tp-csat-block">${csat_html}</div>
616
651
  <button id="tp-new-conversation" style="padding:10px 20px;background:linear-gradient(135deg,${color},${dark});color:white;border:none;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;margin-bottom:8px;width:100%;">
617
652
  💬 Nouvelle conversation
618
653
  </button>
@@ -624,6 +659,26 @@
624
659
  container.appendChild(banner);
625
660
  container.scrollTop = container.scrollHeight;
626
661
 
662
+ if (!existing_csat) {
663
+ const submit_csat = async rating => {
664
+ const csat_block = this.querySelector('#tp-csat-block');
665
+ if (!csat_block) return;
666
+ try {
667
+ await fetch(`${this.api_url}/chat/conversations/${this.conversation_id}/csat`, {
668
+ method: 'POST',
669
+ headers: this.getHeaders(true),
670
+ body: JSON.stringify({ user_id: this.user_id, rating }),
671
+ });
672
+ } catch (e) {
673
+ console.error('CSAT error:', e);
674
+ }
675
+ 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>`;
676
+ };
677
+
678
+ this.querySelector('#tp-csat-positive').addEventListener('click', () => submit_csat('positive'));
679
+ this.querySelector('#tp-csat-negative').addEventListener('click', () => submit_csat('negative'));
680
+ }
681
+
627
682
  this.querySelector('#tp-new-conversation').addEventListener('click', () => this.startNewConversation());
628
683
  this.querySelector('#tp-back-to-list').addEventListener('click', () => this.showConversationList());
629
684
  }
@@ -663,20 +718,29 @@
663
718
  this.hideTyping();
664
719
 
665
720
  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';
721
+ this.addMessage('system', data.error?.message || 'Trop de messages. Veuillez patienter avant de réessayer.');
669
722
  this.is_loading = false;
670
723
  return;
671
724
  }
672
725
 
673
726
  this.agent_mode = data.result.agent_mode;
727
+ const source = data.result.source || null;
728
+
674
729
  const subtitle = this.querySelector('#tp-subtitle');
675
730
  if (subtitle) subtitle.textContent = this.agent_mode ? '👤 Agent humain' : '🤖 Assistant virtuel';
676
731
 
677
732
  if (data.result.reply) {
678
733
  this.addMessage(this.agent_mode ? 'agent' : 'assistant', data.result.reply);
679
734
  }
735
+
736
+ // Afficher bouton agent si fallback ou ≥ 3 messages user
737
+ const user_msg_count = this.messages.filter(m => m.role === 'user').length;
738
+ const agent_bar = this.querySelector('#tp-agent-bar');
739
+ if (agent_bar && !this.agent_requested && !this.agent_mode) {
740
+ if (source === 'fallback' || user_msg_count >= 5) {
741
+ agent_bar.style.display = 'block';
742
+ }
743
+ }
680
744
  } catch {
681
745
  this.hideTyping();
682
746
  this.addMessage('assistant', 'Une erreur est survenue. Veuillez réessayer.');