@developpement/tp-chatbot-widget 0.0.10 → 0.0.12
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 -0
- package/src/chatbot.js +536 -382
package/src/chatbot.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
// ─── SVG Icons ──────────────────────────────────────────────────────────────
|
|
5
|
-
// viewBox 0 0 20 20 | stroke-width 1.6 | round caps & joins | fill none | stroke currentColor
|
|
6
|
-
|
|
7
4
|
const ICONS = {
|
|
8
5
|
bell: `<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M10 2a6 6 0 0 0-6 6c0 3.5-1.5 5-1.5 5h15s-1.5-1.5-1.5-5a6 6 0 0 0-6-6Z"/><path d="M11.73 17a2 2 0 0 1-3.46 0"/></svg>`,
|
|
9
6
|
bell_off: `<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M4 4 16 16M8.27 3.05A6 6 0 0 1 16 8c0 2.2.5 3.7 1 4.7M6.15 6.17A5.97 5.97 0 0 0 4 8c0 3.5-1.5 5-1.5 5H14M11.73 17a2 2 0 0 1-3.46 0"/></svg>`,
|
|
@@ -15,12 +12,13 @@
|
|
|
15
12
|
agent: `<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M3 10a7 7 0 1 1 14 0"/><path d="M2 10h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1Z"/><path d="M16 10h1a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-1a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1Z"/><path d="M16 13v1a3 3 0 0 1-3 3h-2"/></svg>`,
|
|
16
13
|
bot: `<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="7" width="14" height="10" rx="2"/><path d="M7 11h.01M13 11h.01M7 14.5h6"/><circle cx="10" cy="4" r="1.5"/><line x1="10" y1="5.5" x2="10" y2="7"/></svg>`,
|
|
17
14
|
send: `<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3 3 9l5 2 2 5 7-13Z"/><path d="m8 11 4-4"/></svg>`,
|
|
18
|
-
attach: `<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M16.5 10.5 9 18a5 5 0 0 1-7-7l8.5-8.5a3 3 0 0 1 4.24 4.24L6 15.5a1 1 0 0 1-1.42-1.42L12 7"/></svg>`,
|
|
19
15
|
helpful: `<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M7 10V17M7 10l3-7a3 3 0 0 1 3 3v2h3.5a1 1 0 0 1 1 1.15l-.9 5A1 1 0 0 1 15.6 17H7"/><rect x="3" y="10" width="4" height="7" rx="1"/></svg>`,
|
|
20
16
|
not_helpful: `<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M13 10V3M13 10l-3 7a3 3 0 0 1-3-3v-2H3.5a1 1 0 0 1-1-1.15l.9-5A1 1 0 0 1 4.4 3H13"/><rect x="13" y="3" width="4" height="7" rx="1"/></svg>`,
|
|
21
17
|
check: `<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M4 10l5 5 7-8"/></svg>`,
|
|
22
18
|
msg_plus: `<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M14 3H6a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h2l2 3 2-3h2a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2Z"/><path d="M10 7v5M7.5 9.5h5"/></svg>`,
|
|
23
19
|
msg: `<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M14 3H6a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h2l2 3 2-3h2a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2Z"/></svg>`,
|
|
20
|
+
history: `<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M3 10a7 7 0 1 0 7-7 7 7 0 0 0-5 2.1L3 7"/><path d="M3 3v4h4"/><path d="M10 7v4l2.5 2.5"/></svg>`,
|
|
21
|
+
lock: `<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="9" width="12" height="9" rx="2"/><path d="M7 9V6a3 3 0 0 1 6 0v3"/></svg>`,
|
|
24
22
|
};
|
|
25
23
|
|
|
26
24
|
function icon(name, size = 18, extra_style = '') {
|
|
@@ -32,7 +30,114 @@
|
|
|
32
30
|
);
|
|
33
31
|
}
|
|
34
32
|
|
|
35
|
-
|
|
33
|
+
function injectCSS() {
|
|
34
|
+
if (document.getElementById('tp-chatbot-base-css')) return;
|
|
35
|
+
const style = document.createElement('style');
|
|
36
|
+
style.id = 'tp-chatbot-base-css';
|
|
37
|
+
style.textContent = `
|
|
38
|
+
tp-chatbot * { box-sizing: border-box; margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; }
|
|
39
|
+
.tp-chatbot-host { position: fixed; bottom: 24px; z-index: 9999; display: flex; flex-direction: column; align-items: flex-end; width: 380px; }
|
|
40
|
+
.tp-chatbot-window { position: absolute; bottom: 72px; left: 0; right: auto; width: 380px; height: 580px; background: #fff; border-radius: 20px; box-shadow: 0 8px 40px rgba(0,0,0,0.14); display: flex; flex-direction: column; overflow: hidden; opacity: 0; transform: scale(0.92) translateY(12px); pointer-events: none; transition: opacity 0.22s ease, transform 0.22s ease; }
|
|
41
|
+
.tp-chatbot-window.open { opacity: 1; transform: scale(1) translateY(0); pointer-events: all; }
|
|
42
|
+
.tp-chatbot-bubble { width: 60px; height: 60px; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: transform 0.18s, box-shadow 0.18s; margin-left: auto; flex-shrink: 0; }
|
|
43
|
+
.tp-chatbot-bubble:hover { transform: scale(1.07); }
|
|
44
|
+
.tp-header { padding: 14px 16px 0; color: white; }
|
|
45
|
+
.tp-header-top { display: flex; align-items: center; gap: 10px; padding-bottom: 12px; }
|
|
46
|
+
.tp-header-avatar { width: 38px; height: 38px; border-radius: 50%; background: rgba(255,255,255,0.25); display: flex; align-items: center; justify-content: center; font-size: 15px; font-weight: 700; color: white; position: relative; flex-shrink: 0; }
|
|
47
|
+
.tp-header-dot { position: absolute; bottom: 1px; right: 1px; width: 9px; height: 9px; border-radius: 50%; background: #22c55e; border: 2px solid white; }
|
|
48
|
+
.tp-header-info { flex: 1; min-width: 0; }
|
|
49
|
+
.tp-header-title { font-size: 14px; font-weight: 700; color: white; line-height: 1.2; }
|
|
50
|
+
.tp-header-subtitle { font-size: 11px; color: rgba(255,255,255,0.8); display: flex; align-items: center; gap: 4px; margin-top: 2px; }
|
|
51
|
+
.tp-header-btn { background: rgba(255,255,255,0.18); border: none; border-radius: 8px; width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; cursor: pointer; color: white; transition: background 0.15s; flex-shrink: 0; }
|
|
52
|
+
.tp-header-btn:hover { background: rgba(255,255,255,0.32); }
|
|
53
|
+
.tp-header-controls { display: flex; align-items: center; gap: 6px; }
|
|
54
|
+
.tp-tabs { display: flex; gap: 0; padding: 0 16px; border-bottom: 1px solid rgba(255,255,255,0.2); margin-top: 2px; justify-content: center; }
|
|
55
|
+
.tp-tab { padding: 8px 14px; font-size: 12px; font-weight: 600; color: rgba(255,255,255,0.65); border: none; background: none; cursor: pointer; border-bottom: 2px solid transparent; transition: color 0.15s, border-color 0.15s; display: flex; align-items: center; gap: 5px; margin-bottom: -1px; }
|
|
56
|
+
.tp-tab.active { color: white; border-bottom-color: white; }
|
|
57
|
+
.tp-body { flex: 1; overflow-y: auto; background: #f7f7f8; display: flex; flex-direction: column; }
|
|
58
|
+
.tp-body::-webkit-scrollbar { width: 4px; }
|
|
59
|
+
.tp-body::-webkit-scrollbar-thumb { background: #ddd; border-radius: 2px; }
|
|
60
|
+
.tp-messages-wrap { flex: 1; overflow-y: auto; padding: 16px 14px; display: flex; flex-direction: column; gap: 10px; background: #f7f7f8; }
|
|
61
|
+
.tp-messages-wrap::-webkit-scrollbar { width: 4px; }
|
|
62
|
+
.tp-messages-wrap::-webkit-scrollbar-thumb { background: #ddd; border-radius: 2px; }
|
|
63
|
+
.tp-msg { display: flex; flex-direction: column; max-width: 82%; }
|
|
64
|
+
.tp-msg.user { align-self: flex-end; align-items: flex-end; }
|
|
65
|
+
.tp-msg.assistant, .tp-msg.agent, .tp-msg.system { align-self: flex-start; align-items: flex-start; }
|
|
66
|
+
.tp-msg-role { font-size: 10px; color: #aaa; margin-bottom: 3px; display: flex; align-items: center; gap: 3px; }
|
|
67
|
+
.tp-msg.user .tp-msg-role { color: #aaa; }
|
|
68
|
+
.tp-bubble-msg { padding: 10px 13px; border-radius: 16px; font-size: 13px; line-height: 1.55; word-break: break-word; }
|
|
69
|
+
.tp-msg.user .tp-bubble-msg { color: white; border-bottom-right-radius: 4px; }
|
|
70
|
+
.tp-msg.assistant .tp-bubble-msg, .tp-msg.agent .tp-bubble-msg { background: white; color: #1a1a2e; border-bottom-left-radius: 4px; box-shadow: 0 1px 4px rgba(0,0,0,0.07); }
|
|
71
|
+
.tp-msg.system .tp-bubble-msg { background: #e8f5e9; color: #2e7d32; font-size: 12px; border-radius: 10px; padding: 8px 12px; }
|
|
72
|
+
.tp-msg-time { font-size: 10px; color: #bbb; margin-top: 3px; }
|
|
73
|
+
.tp-md p { margin-bottom: 6px; }
|
|
74
|
+
.tp-md p:last-child { margin-bottom: 0; }
|
|
75
|
+
.tp-md ul, .tp-md ol { padding-left: 18px; margin-bottom: 6px; }
|
|
76
|
+
.tp-md li { margin-bottom: 2px; }
|
|
77
|
+
.tp-md strong { font-weight: 600; }
|
|
78
|
+
.tp-md code { background: #f0f0f0; border-radius: 4px; padding: 1px 5px; font-size: 12px; }
|
|
79
|
+
.tp-md pre { background: #f0f0f0; border-radius: 8px; padding: 10px; overflow-x: auto; margin-bottom: 6px; }
|
|
80
|
+
.tp-feedback-bar { display: flex; gap: 6px; margin-top: 7px; }
|
|
81
|
+
.tp-feedback-btn { background: none; border: 1px solid #e5e5e5; border-radius: 8px; padding: 4px 8px; cursor: pointer; color: #aaa; display: flex; align-items: center; transition: all 0.15s; font-size: 11px; }
|
|
82
|
+
.tp-feedback-btn:hover { border-color: #bbb; color: #666; }
|
|
83
|
+
.tp-feedback-btn.voted-positive { border-color: #22c55e; color: #22c55e; background: #f0fdf4; }
|
|
84
|
+
.tp-feedback-btn.voted-negative { border-color: #ef4444; color: #ef4444; background: #fef2f2; }
|
|
85
|
+
.tp-typing { display: flex; align-items: center; gap: 4px; padding: 4px 2px; }
|
|
86
|
+
.tp-typing span { width: 7px; height: 7px; border-radius: 50%; background: #ccc; animation: tp-bounce 1.2s infinite ease-in-out; }
|
|
87
|
+
.tp-typing span:nth-child(2) { animation-delay: 0.18s; }
|
|
88
|
+
.tp-typing span:nth-child(3) { animation-delay: 0.36s; }
|
|
89
|
+
@keyframes tp-bounce { 0%,60%,100%{transform:translateY(0)} 30%{transform:translateY(-5px)} }
|
|
90
|
+
.tp-agent-bar { padding: 8px 14px; border-top: 1px solid #f0f0f0; background: white; }
|
|
91
|
+
.tp-agent-btn { width: 100%; padding: 9px 14px; border-radius: 10px; border: 1.5px solid; background: white; font-size: 12px; font-weight: 600; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 7px; transition: all 0.15s; font-family: inherit; }
|
|
92
|
+
.tp-agent-btn:hover { color: white !important; }
|
|
93
|
+
.tp-close-bar { padding: 8px 14px; border-top: 1px solid #f0f0f0; background: white; text-align: center; }
|
|
94
|
+
.tp-close-btn { background: none; border: none; font-size: 12px; color: #aaa; cursor: pointer; display: inline-flex; align-items: center; gap: 5px; font-family: inherit; padding: 4px 8px; border-radius: 8px; transition: background 0.15s; }
|
|
95
|
+
.tp-close-btn:hover { background: #f5f5f5; color: #888; }
|
|
96
|
+
.tp-input-bar { display: flex; align-items: flex-end; gap: 8px; padding: 10px 12px; border-top: 1px solid #f0f0f0; background: white; }
|
|
97
|
+
.tp-input { flex: 1; padding: 9px 12px; border: 1.5px solid #e5e5e5; border-radius: 12px; font-size: 13px; font-family: inherit; resize: none; outline: none; max-height: 100px; line-height: 1.4; transition: border-color 0.15s; background: #fafafa; }
|
|
98
|
+
.tp-input:focus { border-color: inherit; background: white; }
|
|
99
|
+
.tp-send { width: 36px; height: 36px; border-radius: 10px; border: none; display: flex; align-items: center; justify-content: center; cursor: pointer; flex-shrink: 0; transition: transform 0.15s; }
|
|
100
|
+
.tp-send:hover { transform: scale(1.06); }
|
|
101
|
+
.tp-conv-list { display: flex; flex-direction: column; flex: 1; }
|
|
102
|
+
.tp-conv-item { padding: 13px 16px; border-bottom: 1px solid #f2f2f2; cursor: pointer; transition: background 0.13s; display: flex; justify-content: space-between; align-items: center; background: white; }
|
|
103
|
+
.tp-conv-item:hover { background: #fafafa; }
|
|
104
|
+
.tp-conv-item-left { display: flex; flex-direction: column; gap: 3px; }
|
|
105
|
+
.tp-conv-item-date { font-size: 12px; font-weight: 600; color: #1a1a2e; }
|
|
106
|
+
.tp-conv-item-count { font-size: 11px; color: #bbb; }
|
|
107
|
+
.tp-badge { font-size: 10px; font-weight: 700; padding: 3px 9px; border-radius: 20px; display: inline-flex; align-items: center; gap: 3px; }
|
|
108
|
+
.tp-new-conv-btn { width: 100%; padding: 11px; color: white; border: none; border-radius: 12px; font-size: 13px; font-weight: 700; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 8px; font-family: inherit; transition: opacity 0.15s; }
|
|
109
|
+
.tp-new-conv-btn:hover { opacity: 0.9; }
|
|
110
|
+
.tp-guest-wrap { padding: 16px; display: flex; flex-direction: column; gap: 10px; flex: 1; }
|
|
111
|
+
.tp-guest-intro { background: white; border-radius: 14px; padding: 14px; box-shadow: 0 1px 6px rgba(0,0,0,0.07); }
|
|
112
|
+
.tp-guest-intro-name { font-size: 13px; font-weight: 700; color: #1a1a2e; margin-bottom: 4px; display: flex; align-items: center; gap: 6px; }
|
|
113
|
+
.tp-guest-intro-sub { font-size: 12px; color: #888; line-height: 1.5; }
|
|
114
|
+
.tp-guest-field-label { font-size: 11px; font-weight: 600; color: #666; margin-bottom: 4px; display: block; }
|
|
115
|
+
.tp-guest-input { width: 100%; padding: 10px 13px; border: 1.5px solid #e8e8e8; border-radius: 10px; font-size: 13px; font-family: inherit; outline: none; background: white; transition: border-color 0.15s; }
|
|
116
|
+
.tp-guest-input:focus { border-color: inherit; }
|
|
117
|
+
.tp-guest-submit { width: 100%; padding: 11px; color: white; border: none; border-radius: 12px; font-size: 13px; font-weight: 700; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 8px; font-family: inherit; margin-top: 2px; transition: opacity 0.15s; }
|
|
118
|
+
.tp-guest-submit:hover { opacity: 0.9; }
|
|
119
|
+
.tp-guest-footer { text-align: center; font-size: 10px; color: #ccc; display: flex; align-items: center; justify-content: center; gap: 4px; }
|
|
120
|
+
.tp-guest-error { font-size: 12px; color: #ef4444; display: none; text-align: center; }
|
|
121
|
+
.tp-closed-banner { margin: 12px; padding: 16px; background: white; border: 1px solid #f0f0f0; border-radius: 16px; text-align: center; box-shadow: 0 1px 6px rgba(0,0,0,0.06); }
|
|
122
|
+
.tp-closed-title { font-size: 13px; font-weight: 700; color: #1a1a2e; margin-bottom: 12px; display: flex; align-items: center; justify-content: center; gap: 6px; }
|
|
123
|
+
.tp-closed-date { font-size: 11px; color: #aaa; margin-top: -8px; margin-bottom: 12px; }
|
|
124
|
+
.tp-csat-q { font-size: 12px; color: #888; margin-bottom: 10px; }
|
|
125
|
+
.tp-csat-btns { display: flex; gap: 10px; justify-content: center; margin-bottom: 14px; }
|
|
126
|
+
.tp-csat-btn { flex: 1; padding: 10px; border-radius: 10px; border: 1.5px solid; background: white; cursor: pointer; font-size: 13px; font-weight: 600; display: flex; align-items: center; justify-content: center; gap: 6px; transition: all 0.15s; font-family: inherit; }
|
|
127
|
+
.tp-csat-btn.positive { border-color: #22c55e; color: #22c55e; }
|
|
128
|
+
.tp-csat-btn.positive:hover { background: #22c55e; color: white; }
|
|
129
|
+
.tp-csat-btn.negative { border-color: #ef4444; color: #ef4444; }
|
|
130
|
+
.tp-csat-btn.negative:hover { background: #ef4444; color: white; }
|
|
131
|
+
.tp-csat-thanks { font-size: 13px; color: #888; margin-bottom: 14px; }
|
|
132
|
+
.tp-closed-new-btn { width: 100%; padding: 11px; color: white; border: none; border-radius: 10px; font-size: 13px; font-weight: 700; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 8px; font-family: inherit; margin-bottom: 8px; transition: opacity 0.15s; }
|
|
133
|
+
.tp-closed-new-btn:hover { opacity: 0.9; }
|
|
134
|
+
.tp-closed-hist-btn { width: 100%; padding: 9px; background: none; border: 1px solid #e8e8e8; border-radius: 10px; font-size: 12px; font-weight: 600; cursor: pointer; font-family: inherit; color: #888; transition: background 0.15s; }
|
|
135
|
+
.tp-closed-hist-btn:hover { background: #f5f5f5; }
|
|
136
|
+
.tp-suggestions { display: flex; flex-direction: column; gap: 6px; padding: 4px 0 6px; }
|
|
137
|
+
.tp-suggestion-btn { padding: 8px 14px; border-radius: 20px; border: 1.5px solid; background: white; font-size: 12px; font-weight: 600; cursor: pointer; text-align: left; font-family: inherit; transition: all 0.12s; }
|
|
138
|
+
`;
|
|
139
|
+
document.head.appendChild(style);
|
|
140
|
+
}
|
|
36
141
|
|
|
37
142
|
const CLIENT_THEMES = {
|
|
38
143
|
flix: {
|
|
@@ -116,8 +221,8 @@
|
|
|
116
221
|
};
|
|
117
222
|
|
|
118
223
|
const DEFAULT_THEME = { primary: '#7b1fa2', name: 'Support', position: 'right', suggestions: [] };
|
|
119
|
-
const WINDOW_WIDTH = '
|
|
120
|
-
const WINDOW_HEIGHT = '
|
|
224
|
+
const WINDOW_WIDTH = '340px';
|
|
225
|
+
const WINDOW_HEIGHT = '480px';
|
|
121
226
|
|
|
122
227
|
function getClientTheme(client_id) {
|
|
123
228
|
return CLIENT_THEMES[client_id] || DEFAULT_THEME;
|
|
@@ -143,8 +248,8 @@
|
|
|
143
248
|
function playSound() {
|
|
144
249
|
try {
|
|
145
250
|
const ctx = new (window.AudioContext || window.webkitAudioContext)();
|
|
146
|
-
const osc = ctx.createOscillator()
|
|
147
|
-
|
|
251
|
+
const osc = ctx.createOscillator(),
|
|
252
|
+
gain = ctx.createGain();
|
|
148
253
|
osc.connect(gain);
|
|
149
254
|
gain.connect(ctx.destination);
|
|
150
255
|
osc.frequency.value = 880;
|
|
@@ -156,7 +261,9 @@
|
|
|
156
261
|
} catch {}
|
|
157
262
|
}
|
|
158
263
|
|
|
159
|
-
|
|
264
|
+
function formatTime(iso) {
|
|
265
|
+
return new Date(iso).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
|
266
|
+
}
|
|
160
267
|
|
|
161
268
|
class TpChatbot extends HTMLElement {
|
|
162
269
|
constructor() {
|
|
@@ -179,6 +286,7 @@
|
|
|
179
286
|
this.access_token = '';
|
|
180
287
|
this.is_fullscreen = false;
|
|
181
288
|
this.is_minimized = false;
|
|
289
|
+
this.active_tab = 'new';
|
|
182
290
|
}
|
|
183
291
|
|
|
184
292
|
static get observedAttributes() {
|
|
@@ -195,9 +303,9 @@
|
|
|
195
303
|
this.user_info = extractUserInfo(new_val);
|
|
196
304
|
this.user_id = this.user_info?.user_id;
|
|
197
305
|
this.applyTheme();
|
|
198
|
-
const
|
|
199
|
-
if (
|
|
200
|
-
|
|
306
|
+
const guest_wrap = this.querySelector('#tp-guest-wrap');
|
|
307
|
+
if (guest_wrap) {
|
|
308
|
+
guest_wrap.remove();
|
|
201
309
|
this.showConversationList();
|
|
202
310
|
}
|
|
203
311
|
}
|
|
@@ -214,6 +322,7 @@
|
|
|
214
322
|
if (t.primary) CLIENT_THEMES[this.client_id].primary = t.primary;
|
|
215
323
|
if (t.position) CLIENT_THEMES[this.client_id].position = t.position;
|
|
216
324
|
if (t.name) CLIENT_THEMES[this.client_id].name = t.name;
|
|
325
|
+
if (t.open_by_default !== undefined) CLIENT_THEMES[this.client_id].open_by_default = t.open_by_default;
|
|
217
326
|
} else {
|
|
218
327
|
CLIENT_THEMES[this.client_id] = {
|
|
219
328
|
primary: t.primary || DEFAULT_THEME.primary,
|
|
@@ -223,7 +332,7 @@
|
|
|
223
332
|
};
|
|
224
333
|
}
|
|
225
334
|
} catch (e) {
|
|
226
|
-
console.warn('[tp-chatbot] fetchTheme failed
|
|
335
|
+
console.warn('[tp-chatbot] fetchTheme failed:', e.message);
|
|
227
336
|
}
|
|
228
337
|
}
|
|
229
338
|
|
|
@@ -240,6 +349,9 @@
|
|
|
240
349
|
this.view = 'list';
|
|
241
350
|
this.is_fullscreen = false;
|
|
242
351
|
this.is_minimized = false;
|
|
352
|
+
this.active_tab = 'new';
|
|
353
|
+
|
|
354
|
+
injectCSS();
|
|
243
355
|
|
|
244
356
|
this.fetchTheme().then(() => {
|
|
245
357
|
this.render();
|
|
@@ -247,9 +359,8 @@
|
|
|
247
359
|
this.attachEvents();
|
|
248
360
|
this._initialized = true;
|
|
249
361
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
}, 300);
|
|
362
|
+
const theme = getClientTheme(this.client_id);
|
|
363
|
+
if (theme.open_by_default) setTimeout(() => this.toggleChat(), 300);
|
|
253
364
|
|
|
254
365
|
if (this.access_token) {
|
|
255
366
|
this.user_info = extractUserInfo(this.access_token);
|
|
@@ -275,8 +386,6 @@
|
|
|
275
386
|
if (this.polling_interval) clearInterval(this.polling_interval);
|
|
276
387
|
}
|
|
277
388
|
|
|
278
|
-
// ─── i18n ──────────────────────────────────────────────────────────────────
|
|
279
|
-
|
|
280
389
|
getMessages() {
|
|
281
390
|
const lang = (this.user_info?.language || 'EN').toUpperCase();
|
|
282
391
|
const msgs = {
|
|
@@ -302,6 +411,8 @@
|
|
|
302
411
|
close_btn: 'Terminer la conversation',
|
|
303
412
|
speak_agent: 'Parler à un agent',
|
|
304
413
|
write_msg: 'Écrivez votre message...',
|
|
414
|
+
tab_new: 'Nouveau',
|
|
415
|
+
tab_history: 'Historique',
|
|
305
416
|
limit_reached: 'Vous avez atteint la limite de 30 messages. Veuillez contacter un agent ou écrire à support@travelplanet.com.',
|
|
306
417
|
too_many: 'Trop de messages. Veuillez patienter avant de réessayer.',
|
|
307
418
|
error_occurred: 'Une erreur est survenue. Veuillez réessayer.',
|
|
@@ -316,6 +427,17 @@
|
|
|
316
427
|
status_waiting: 'En attente',
|
|
317
428
|
status_bot: 'Bot',
|
|
318
429
|
messages: 'message',
|
|
430
|
+
guest_title: 'Bonjour ! Je suis Maria 👋',
|
|
431
|
+
guest_sub: "Identifiez-vous pour démarrer. Réponse en moins d'une minute.",
|
|
432
|
+
guest_firstname: 'Prénom *',
|
|
433
|
+
guest_company: 'Entreprise *',
|
|
434
|
+
guest_email: 'Email *',
|
|
435
|
+
guest_submit: 'Démarrer la conversation',
|
|
436
|
+
guest_secured: 'Données sécurisées · Propulsé par Makitizy',
|
|
437
|
+
guest_fill: 'Veuillez remplir tous les champs.',
|
|
438
|
+
guest_email_invalid: 'Veuillez entrer un email valide.',
|
|
439
|
+
helpful: 'Utile',
|
|
440
|
+
not_helpful: 'Pas utile',
|
|
319
441
|
},
|
|
320
442
|
EN: {
|
|
321
443
|
closed: 'Conversation closed',
|
|
@@ -339,6 +461,8 @@
|
|
|
339
461
|
close_btn: 'End conversation',
|
|
340
462
|
speak_agent: 'Talk to an agent',
|
|
341
463
|
write_msg: 'Write your message...',
|
|
464
|
+
tab_new: 'New',
|
|
465
|
+
tab_history: 'History',
|
|
342
466
|
limit_reached: 'You have reached the 30 message limit. Please contact an agent or write to support@travelplanet.com.',
|
|
343
467
|
too_many: 'Too many messages. Please wait before retrying.',
|
|
344
468
|
error_occurred: 'An error occurred. Please try again.',
|
|
@@ -353,6 +477,17 @@
|
|
|
353
477
|
status_waiting: 'Waiting',
|
|
354
478
|
status_bot: 'Bot',
|
|
355
479
|
messages: 'message',
|
|
480
|
+
guest_title: "Hello! I'm Maria 👋",
|
|
481
|
+
guest_sub: 'Identify yourself to start. Answer in less than a minute.',
|
|
482
|
+
guest_firstname: 'First name *',
|
|
483
|
+
guest_company: 'Company *',
|
|
484
|
+
guest_email: 'Email *',
|
|
485
|
+
guest_submit: 'Start conversation',
|
|
486
|
+
guest_secured: 'Secured data · Powered by Makitizy',
|
|
487
|
+
guest_fill: 'Please fill in all fields.',
|
|
488
|
+
guest_email_invalid: 'Please enter a valid email.',
|
|
489
|
+
helpful: 'Helpful',
|
|
490
|
+
not_helpful: 'Not helpful',
|
|
356
491
|
},
|
|
357
492
|
DE: {
|
|
358
493
|
closed: 'Gespräch beendet',
|
|
@@ -361,8 +496,8 @@
|
|
|
361
496
|
csat_negative: 'Danke, wir werden uns verbessern.',
|
|
362
497
|
new_conv: 'Neues Gespräch',
|
|
363
498
|
back_to_list: 'Zurück zur Liste',
|
|
364
|
-
agent_taken: 'Ein Agent hat Ihr Gespräch übernommen.
|
|
365
|
-
agent_released: 'Unser virtueller Assistent ist zurück.
|
|
499
|
+
agent_taken: 'Ein Agent hat Ihr Gespräch übernommen.',
|
|
500
|
+
agent_released: 'Unser virtueller Assistent ist zurück.',
|
|
366
501
|
close_confirm: 'Möchten Sie das Gespräch beenden?',
|
|
367
502
|
terminated: 'Gespräch beendet',
|
|
368
503
|
virtual: 'Virtueller Assistent',
|
|
@@ -376,13 +511,15 @@
|
|
|
376
511
|
close_btn: 'Gespräch beenden',
|
|
377
512
|
speak_agent: 'Mit einem Agenten sprechen',
|
|
378
513
|
write_msg: 'Schreiben Sie Ihre Nachricht...',
|
|
514
|
+
tab_new: 'Neu',
|
|
515
|
+
tab_history: 'Verlauf',
|
|
379
516
|
limit_reached: 'Sie haben das Limit von 30 Nachrichten erreicht.',
|
|
380
517
|
too_many: 'Zu viele Nachrichten. Bitte warten Sie.',
|
|
381
|
-
error_occurred: 'Ein Fehler ist aufgetreten.
|
|
518
|
+
error_occurred: 'Ein Fehler ist aufgetreten.',
|
|
382
519
|
outside_hours: next => `Unser Support ist derzeit geschlossen. Nächster Termin: ${next}.`,
|
|
383
|
-
no_agents: 'Derzeit kein Agent verfügbar.
|
|
384
|
-
waiting_agent: 'Ihre Anfrage wurde übermittelt.
|
|
385
|
-
waiting_agent_already: 'Sie sind bereits in der Warteschlange.
|
|
520
|
+
no_agents: 'Derzeit kein Agent verfügbar.',
|
|
521
|
+
waiting_agent: 'Ihre Anfrage wurde übermittelt.',
|
|
522
|
+
waiting_agent_already: 'Sie sind bereits in der Warteschlange.',
|
|
386
523
|
agent_already: 'Ein Agent betreut bereits Ihr Gespräch.',
|
|
387
524
|
suggest_agent_escalation: 'Ich kann keine passende Antwort finden. Möchten Sie mit einem Agenten sprechen?',
|
|
388
525
|
status_closed: 'Geschlossen',
|
|
@@ -390,6 +527,17 @@
|
|
|
390
527
|
status_waiting: 'Wartend',
|
|
391
528
|
status_bot: 'Bot',
|
|
392
529
|
messages: 'Nachricht',
|
|
530
|
+
guest_title: 'Hallo! Ich bin Maria 👋',
|
|
531
|
+
guest_sub: 'Identifizieren Sie sich, um zu starten.',
|
|
532
|
+
guest_firstname: 'Vorname *',
|
|
533
|
+
guest_company: 'Unternehmen *',
|
|
534
|
+
guest_email: 'E-Mail *',
|
|
535
|
+
guest_submit: 'Gespräch starten',
|
|
536
|
+
guest_secured: 'Gesicherte Daten · Powered by Makitizy',
|
|
537
|
+
guest_fill: 'Bitte füllen Sie alle Felder aus.',
|
|
538
|
+
guest_email_invalid: 'Bitte geben Sie eine gültige E-Mail ein.',
|
|
539
|
+
helpful: 'Hilfreich',
|
|
540
|
+
not_helpful: 'Nicht hilfreich',
|
|
393
541
|
},
|
|
394
542
|
ES: {
|
|
395
543
|
closed: 'Conversación cerrada',
|
|
@@ -398,14 +546,14 @@
|
|
|
398
546
|
csat_negative: 'Gracias, mejoraremos.',
|
|
399
547
|
new_conv: 'Nueva conversación',
|
|
400
548
|
back_to_list: 'Volver a las conversaciones',
|
|
401
|
-
agent_taken: 'Un agente ha tomado su conversación.
|
|
402
|
-
agent_released: 'Nuestro asistente virtual ha vuelto.
|
|
549
|
+
agent_taken: 'Un agente ha tomado su conversación.',
|
|
550
|
+
agent_released: 'Nuestro asistente virtual ha vuelto.',
|
|
403
551
|
close_confirm: '¿Desea finalizar esta conversación?',
|
|
404
552
|
terminated: 'Conversación terminada',
|
|
405
553
|
virtual: 'Asistente virtual',
|
|
406
554
|
human_agent: 'Agente humano',
|
|
407
555
|
loading: 'Cargando...',
|
|
408
|
-
no_conv: 'No hay conversaciones
|
|
556
|
+
no_conv: 'No hay conversaciones.',
|
|
409
557
|
new_conv_btn: 'Nueva conversación',
|
|
410
558
|
error_load: 'Error de carga.',
|
|
411
559
|
recent_conv: 'Sus conversaciones recientes',
|
|
@@ -413,20 +561,33 @@
|
|
|
413
561
|
close_btn: 'Finalizar conversación',
|
|
414
562
|
speak_agent: 'Hablar con un agente',
|
|
415
563
|
write_msg: 'Escriba su mensaje...',
|
|
564
|
+
tab_new: 'Nuevo',
|
|
565
|
+
tab_history: 'Historial',
|
|
416
566
|
limit_reached: 'Ha alcanzado el límite de 30 mensajes.',
|
|
417
|
-
too_many: 'Demasiados mensajes.
|
|
418
|
-
error_occurred: 'Se produjo un error.
|
|
567
|
+
too_many: 'Demasiados mensajes.',
|
|
568
|
+
error_occurred: 'Se produjo un error.',
|
|
419
569
|
outside_hours: next => `Nuestro soporte está cerrado. Próximo horario: ${next}.`,
|
|
420
|
-
no_agents: 'No hay agentes disponibles.
|
|
421
|
-
waiting_agent: 'Su solicitud ha sido enviada.
|
|
422
|
-
waiting_agent_already: 'Ya está en la cola
|
|
570
|
+
no_agents: 'No hay agentes disponibles.',
|
|
571
|
+
waiting_agent: 'Su solicitud ha sido enviada.',
|
|
572
|
+
waiting_agent_already: 'Ya está en la cola.',
|
|
423
573
|
agent_already: 'Un agente ya está gestionando su conversación.',
|
|
424
|
-
suggest_agent_escalation: '
|
|
574
|
+
suggest_agent_escalation: '¿Desea hablar con un agente?',
|
|
425
575
|
status_closed: 'Cerrado',
|
|
426
576
|
status_agent: 'Agente',
|
|
427
577
|
status_waiting: 'Esperando',
|
|
428
578
|
status_bot: 'Bot',
|
|
429
579
|
messages: 'mensaje',
|
|
580
|
+
guest_title: '¡Hola! Soy Maria 👋',
|
|
581
|
+
guest_sub: 'Identifíquese para comenzar.',
|
|
582
|
+
guest_firstname: 'Nombre *',
|
|
583
|
+
guest_company: 'Empresa *',
|
|
584
|
+
guest_email: 'Email *',
|
|
585
|
+
guest_submit: 'Iniciar conversación',
|
|
586
|
+
guest_secured: 'Datos seguros · Powered by Makitizy',
|
|
587
|
+
guest_fill: 'Complete todos los campos.',
|
|
588
|
+
guest_email_invalid: 'Email inválido.',
|
|
589
|
+
helpful: 'Útil',
|
|
590
|
+
not_helpful: 'No útil',
|
|
430
591
|
},
|
|
431
592
|
IT: {
|
|
432
593
|
closed: 'Conversazione chiusa',
|
|
@@ -436,13 +597,13 @@
|
|
|
436
597
|
new_conv: 'Nuova conversazione',
|
|
437
598
|
back_to_list: 'Torna alle conversazioni',
|
|
438
599
|
agent_taken: 'Un agente ha preso in carico la sua conversazione.',
|
|
439
|
-
agent_released: 'Il nostro assistente virtuale è tornato.
|
|
600
|
+
agent_released: 'Il nostro assistente virtuale è tornato.',
|
|
440
601
|
close_confirm: 'Vuole terminare questa conversazione?',
|
|
441
602
|
terminated: 'Conversazione terminata',
|
|
442
603
|
virtual: 'Assistente virtuale',
|
|
443
604
|
human_agent: 'Agente umano',
|
|
444
605
|
loading: 'Caricamento...',
|
|
445
|
-
no_conv: 'Nessuna conversazione
|
|
606
|
+
no_conv: 'Nessuna conversazione.',
|
|
446
607
|
new_conv_btn: 'Nuova conversazione',
|
|
447
608
|
error_load: 'Errore di caricamento.',
|
|
448
609
|
recent_conv: 'Le sue conversazioni recenti',
|
|
@@ -450,20 +611,33 @@
|
|
|
450
611
|
close_btn: 'Termina conversazione',
|
|
451
612
|
speak_agent: 'Parla con un agente',
|
|
452
613
|
write_msg: 'Scrivi il tuo messaggio...',
|
|
614
|
+
tab_new: 'Nuovo',
|
|
615
|
+
tab_history: 'Cronologia',
|
|
453
616
|
limit_reached: 'Hai raggiunto il limite di 30 messaggi.',
|
|
454
|
-
too_many: 'Troppi messaggi.
|
|
455
|
-
error_occurred: 'Si è verificato un errore.
|
|
617
|
+
too_many: 'Troppi messaggi.',
|
|
618
|
+
error_occurred: 'Si è verificato un errore.',
|
|
456
619
|
outside_hours: next => `Il supporto è chiuso. Prossima disponibilità: ${next}.`,
|
|
457
|
-
no_agents: 'Nessun agente disponibile.
|
|
458
|
-
waiting_agent: 'Richiesta inviata.
|
|
459
|
-
waiting_agent_already: 'È già in coda.
|
|
620
|
+
no_agents: 'Nessun agente disponibile.',
|
|
621
|
+
waiting_agent: 'Richiesta inviata.',
|
|
622
|
+
waiting_agent_already: 'È già in coda.',
|
|
460
623
|
agent_already: 'Un agente sta già gestendo la conversazione.',
|
|
461
|
-
suggest_agent_escalation: '
|
|
624
|
+
suggest_agent_escalation: 'Desidera parlare con un agente?',
|
|
462
625
|
status_closed: 'Chiuso',
|
|
463
626
|
status_agent: 'Agente',
|
|
464
627
|
status_waiting: 'Attesa',
|
|
465
628
|
status_bot: 'Bot',
|
|
466
629
|
messages: 'messaggio',
|
|
630
|
+
guest_title: 'Ciao! Sono Maria 👋',
|
|
631
|
+
guest_sub: 'Si identifichi per iniziare.',
|
|
632
|
+
guest_firstname: 'Nome *',
|
|
633
|
+
guest_company: 'Azienda *',
|
|
634
|
+
guest_email: 'Email *',
|
|
635
|
+
guest_submit: 'Inizia conversazione',
|
|
636
|
+
guest_secured: 'Dati sicuri · Powered by Makitizy',
|
|
637
|
+
guest_fill: 'Compila tutti i campi.',
|
|
638
|
+
guest_email_invalid: 'Email non valida.',
|
|
639
|
+
helpful: 'Utile',
|
|
640
|
+
not_helpful: 'Non utile',
|
|
467
641
|
},
|
|
468
642
|
NL: {
|
|
469
643
|
closed: 'Gesprek gesloten',
|
|
@@ -473,13 +647,13 @@
|
|
|
473
647
|
new_conv: 'Nieuw gesprek',
|
|
474
648
|
back_to_list: 'Terug naar gesprekken',
|
|
475
649
|
agent_taken: 'Een agent heeft uw gesprek overgenomen.',
|
|
476
|
-
agent_released: 'Onze virtuele assistent is terug.
|
|
650
|
+
agent_released: 'Onze virtuele assistent is terug.',
|
|
477
651
|
close_confirm: 'Wilt u dit gesprek beëindigen?',
|
|
478
652
|
terminated: 'Gesprek beëindigd',
|
|
479
653
|
virtual: 'Virtuele assistent',
|
|
480
654
|
human_agent: 'Menselijke agent',
|
|
481
655
|
loading: 'Laden...',
|
|
482
|
-
no_conv: 'Geen gesprekken
|
|
656
|
+
no_conv: 'Geen gesprekken.',
|
|
483
657
|
new_conv_btn: 'Nieuw gesprek',
|
|
484
658
|
error_load: 'Laadout.',
|
|
485
659
|
recent_conv: 'Uw recente gesprekken',
|
|
@@ -487,20 +661,33 @@
|
|
|
487
661
|
close_btn: 'Gesprek beëindigen',
|
|
488
662
|
speak_agent: 'Praat met een agent',
|
|
489
663
|
write_msg: 'Schrijf uw bericht...',
|
|
664
|
+
tab_new: 'Nieuw',
|
|
665
|
+
tab_history: 'Geschiedenis',
|
|
490
666
|
limit_reached: 'U heeft de limiet van 30 berichten bereikt.',
|
|
491
|
-
too_many: 'Te veel berichten.
|
|
492
|
-
error_occurred: 'Er is een fout opgetreden.
|
|
493
|
-
outside_hours: next => `
|
|
494
|
-
no_agents: 'Geen agent beschikbaar.
|
|
495
|
-
waiting_agent: 'Uw verzoek is verzonden.
|
|
496
|
-
waiting_agent_already: 'U staat al in de wachtrij.
|
|
667
|
+
too_many: 'Te veel berichten.',
|
|
668
|
+
error_occurred: 'Er is een fout opgetreden.',
|
|
669
|
+
outside_hours: next => `Support is gesloten. Volgende tijd: ${next}.`,
|
|
670
|
+
no_agents: 'Geen agent beschikbaar.',
|
|
671
|
+
waiting_agent: 'Uw verzoek is verzonden.',
|
|
672
|
+
waiting_agent_already: 'U staat al in de wachtrij.',
|
|
497
673
|
agent_already: 'Een agent beheert uw gesprek al.',
|
|
498
|
-
suggest_agent_escalation: '
|
|
674
|
+
suggest_agent_escalation: 'Wilt u met een agent spreken?',
|
|
499
675
|
status_closed: 'Gesloten',
|
|
500
676
|
status_agent: 'Agent',
|
|
501
677
|
status_waiting: 'Wachten',
|
|
502
678
|
status_bot: 'Bot',
|
|
503
679
|
messages: 'bericht',
|
|
680
|
+
guest_title: 'Hallo! Ik ben Maria 👋',
|
|
681
|
+
guest_sub: 'Identificeer uzelf om te starten.',
|
|
682
|
+
guest_firstname: 'Voornaam *',
|
|
683
|
+
guest_company: 'Bedrijf *',
|
|
684
|
+
guest_email: 'E-mail *',
|
|
685
|
+
guest_submit: 'Gesprek starten',
|
|
686
|
+
guest_secured: 'Beveiligde gegevens · Powered by Makitizy',
|
|
687
|
+
guest_fill: 'Vul alle velden in.',
|
|
688
|
+
guest_email_invalid: 'Ongeldig e-mailadres.',
|
|
689
|
+
helpful: 'Nuttig',
|
|
690
|
+
not_helpful: 'Niet nuttig',
|
|
504
691
|
},
|
|
505
692
|
};
|
|
506
693
|
return msgs[lang] || msgs['EN'];
|
|
@@ -520,8 +707,6 @@
|
|
|
520
707
|
return msgs[lang] || msgs['EN'];
|
|
521
708
|
}
|
|
522
709
|
|
|
523
|
-
// ─── Theme ─────────────────────────────────────────────────────────────────
|
|
524
|
-
|
|
525
710
|
getThemeColor() {
|
|
526
711
|
return getClientTheme(this.client_id).primary;
|
|
527
712
|
}
|
|
@@ -542,30 +727,28 @@
|
|
|
542
727
|
const style = document.createElement('style');
|
|
543
728
|
style.id = 'tp-chatbot-theme';
|
|
544
729
|
style.textContent = `
|
|
545
|
-
.tp-chatbot-bubble { background: linear-gradient(135deg, ${color}, ${dark}) !important; box-shadow: 0 4px
|
|
546
|
-
.tp-
|
|
547
|
-
.tp-
|
|
548
|
-
.tp-
|
|
549
|
-
.tp-
|
|
550
|
-
.tp-
|
|
551
|
-
.tp-
|
|
552
|
-
.tp-
|
|
553
|
-
.tp-
|
|
554
|
-
.tp-chatbot-agent-btn:hover { background: ${color} !important; color: white !important; }
|
|
555
|
-
.tp-chatbot-typing span { background: ${color} !important; }
|
|
556
|
-
#tp-new-conversation { background: linear-gradient(135deg, ${color}, ${dark}) !important; }
|
|
557
|
-
.tp-conv-item:hover { background: ${color}11 !important; }
|
|
730
|
+
.tp-chatbot-bubble { background: linear-gradient(135deg, ${color}, ${dark}) !important; box-shadow: 0 4px 20px ${color}55 !important; }
|
|
731
|
+
.tp-header { background: linear-gradient(135deg, ${color}, ${dark}) !important; }
|
|
732
|
+
.tp-msg.user .tp-bubble-msg { background: linear-gradient(135deg, ${color}, ${dark}) !important; }
|
|
733
|
+
.tp-input:focus { border-color: ${color} !important; }
|
|
734
|
+
.tp-send { background: linear-gradient(135deg, ${color}, ${dark}) !important; }
|
|
735
|
+
.tp-agent-btn { border-color: ${color} !important; color: ${color} !important; }
|
|
736
|
+
.tp-agent-btn:hover { background: ${color} !important; color: white !important; }
|
|
737
|
+
.tp-typing span { background: ${color} !important; }
|
|
738
|
+
.tp-new-conv-btn { background: linear-gradient(135deg, ${color}, ${dark}) !important; }
|
|
558
739
|
.tp-conv-new-btn { background: linear-gradient(135deg, ${color}, ${dark}) !important; }
|
|
559
|
-
.tp-
|
|
560
|
-
.tp-
|
|
561
|
-
.tp-
|
|
740
|
+
.tp-conv-item:hover { background: ${color}0d !important; }
|
|
741
|
+
.tp-guest-input:focus { border-color: ${color} !important; }
|
|
742
|
+
.tp-guest-submit { background: linear-gradient(135deg, ${color}, ${dark}) !important; }
|
|
743
|
+
.tp-tab.active { border-bottom-color: white !important; color: white !important; }
|
|
744
|
+
.tp-closed-new-btn { background: linear-gradient(135deg, ${color}, ${dark}) !important; }
|
|
745
|
+
.tp-suggestion-btn { border-color: ${color} !important; color: ${color} !important; }
|
|
746
|
+
.tp-suggestion-btn:hover { background: ${color} !important; color: white !important; }
|
|
562
747
|
`;
|
|
563
748
|
document.head.appendChild(style);
|
|
564
|
-
|
|
565
749
|
const position = getClientTheme(this.client_id).position || 'right';
|
|
566
750
|
const host = this.querySelector('.tp-chatbot-host');
|
|
567
751
|
if (host) {
|
|
568
|
-
host.style.width = WINDOW_WIDTH;
|
|
569
752
|
host.style.left = position === 'left' ? '24px' : 'auto';
|
|
570
753
|
host.style.right = position === 'left' ? 'auto' : '24px';
|
|
571
754
|
}
|
|
@@ -574,7 +757,7 @@
|
|
|
574
757
|
bubble.style.marginLeft = position === 'left' ? '0' : 'auto';
|
|
575
758
|
bubble.style.marginRight = position === 'left' ? 'auto' : '0';
|
|
576
759
|
}
|
|
577
|
-
const win = this.querySelector('
|
|
760
|
+
const win = this.querySelector('#tp-window');
|
|
578
761
|
if (win) {
|
|
579
762
|
win.style.left = '0';
|
|
580
763
|
win.style.right = 'auto';
|
|
@@ -582,47 +765,55 @@
|
|
|
582
765
|
}
|
|
583
766
|
}
|
|
584
767
|
|
|
585
|
-
// ─── Render ────────────────────────────────────────────────────────────────
|
|
586
|
-
|
|
587
768
|
render() {
|
|
588
769
|
const theme = getClientTheme(this.client_id);
|
|
770
|
+
const m = this.getMessages();
|
|
589
771
|
this.innerHTML = `
|
|
590
772
|
<div class="tp-chatbot-host">
|
|
591
773
|
<div class="tp-chatbot-window" id="tp-window">
|
|
592
|
-
<div class="tp-
|
|
593
|
-
<div class="tp-
|
|
594
|
-
|
|
595
|
-
<div class="tp-
|
|
596
|
-
|
|
774
|
+
<div class="tp-header" id="tp-header">
|
|
775
|
+
<div class="tp-header-top">
|
|
776
|
+
<div class="tp-header-avatar">M<div class="tp-header-dot"></div></div>
|
|
777
|
+
<div class="tp-header-info">
|
|
778
|
+
<div class="tp-header-title">${theme.name} Support</div>
|
|
779
|
+
<div class="tp-header-subtitle" id="tp-subtitle">${icon('bot', 11)} ${m.virtual}</div>
|
|
780
|
+
</div>
|
|
781
|
+
<div class="tp-header-controls">
|
|
782
|
+
<button id="tp-sound-btn" class="tp-header-btn" title="Sound">${icon('bell', 14)}</button>
|
|
783
|
+
<button id="tp-minimize-btn" class="tp-header-btn" title="Minimize">${icon('minimize', 14)}</button>
|
|
784
|
+
<button id="tp-maximize-btn" class="tp-header-btn" title="Fullscreen">${icon('restore', 14)}</button>
|
|
785
|
+
<button id="tp-back-btn" class="tp-header-btn" style="display:none;" title="Back">${icon('back', 14)}</button>
|
|
786
|
+
<button id="tp-close-win-btn" class="tp-header-btn" title="Close">${icon('close', 14)}</button>
|
|
787
|
+
</div>
|
|
788
|
+
</div>
|
|
789
|
+
<div class="tp-tabs" id="tp-tabs">
|
|
790
|
+
<button class="tp-tab active" data-tab="new" id="tp-tab-new">${icon('msg_plus', 12)} ${m.tab_new}</button>
|
|
791
|
+
<button class="tp-tab" data-tab="history" id="tp-tab-history">${icon('history', 12)} ${m.tab_history}</button>
|
|
597
792
|
</div>
|
|
598
|
-
<button id="tp-sound-btn" class="tp-header-btn" title="Sound">${icon('bell', 16)}</button>
|
|
599
|
-
<button id="tp-minimize-btn" class="tp-header-btn" title="Minimize">${icon('minimize', 16)}</button>
|
|
600
|
-
<button id="tp-maximize-btn" class="tp-header-btn" title="Fullscreen">${icon('restore', 16)}</button>
|
|
601
|
-
<button id="tp-back-btn" class="tp-header-btn" style="display:none;" title="Back">${icon('back', 16)}</button>
|
|
602
793
|
</div>
|
|
603
|
-
<div class="tp-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
794
|
+
<div class="tp-body" id="tp-body">
|
|
795
|
+
<div class="tp-messages-wrap" id="tp-messages"></div>
|
|
796
|
+
</div>
|
|
797
|
+
<div class="tp-agent-bar" id="tp-agent-bar" style="display:none;">
|
|
798
|
+
<button class="tp-agent-btn" id="tp-agent-btn">
|
|
799
|
+
<span style="display:inline-flex;align-items:center;gap:6px;">${icon('agent', 15)} ${m.speak_agent}</span>
|
|
607
800
|
</button>
|
|
608
801
|
</div>
|
|
609
|
-
<div class="tp-
|
|
610
|
-
<textarea class="tp-
|
|
611
|
-
<button class="tp-
|
|
802
|
+
<div class="tp-input-bar" id="tp-input-bar" style="display:none;">
|
|
803
|
+
<textarea class="tp-input" id="tp-input" placeholder="${m.write_msg}" rows="1"></textarea>
|
|
804
|
+
<button class="tp-send" id="tp-send">${icon('send', 15, 'color:white')}</button>
|
|
612
805
|
</div>
|
|
613
|
-
<div id="tp-close-bar" style="
|
|
614
|
-
<button
|
|
615
|
-
<span style="display:inline-flex;align-items:center;gap:
|
|
806
|
+
<div class="tp-close-bar" id="tp-close-bar" style="display:none;">
|
|
807
|
+
<button class="tp-close-btn" id="tp-close-btn">
|
|
808
|
+
<span style="display:inline-flex;align-items:center;gap:5px;">${icon('check', 12)} ${m.close_btn}</span>
|
|
616
809
|
</button>
|
|
617
810
|
</div>
|
|
618
811
|
</div>
|
|
619
|
-
<div class="tp-chatbot-bubble" id="tp-bubble">${icon('msg',
|
|
812
|
+
<div class="tp-chatbot-bubble" id="tp-bubble">${icon('msg', 26, 'color:white')}</div>
|
|
620
813
|
</div>
|
|
621
814
|
`;
|
|
622
815
|
}
|
|
623
816
|
|
|
624
|
-
// ─── Window controls ───────────────────────────────────────────────────────
|
|
625
|
-
|
|
626
817
|
minimize() {
|
|
627
818
|
const win = this.querySelector('#tp-window');
|
|
628
819
|
if (this.is_fullscreen) this.exitFullscreen(false);
|
|
@@ -632,8 +823,8 @@
|
|
|
632
823
|
}
|
|
633
824
|
|
|
634
825
|
enterFullscreen() {
|
|
635
|
-
const win = this.querySelector('#tp-window')
|
|
636
|
-
|
|
826
|
+
const win = this.querySelector('#tp-window'),
|
|
827
|
+
host = this.querySelector('.tp-chatbot-host');
|
|
637
828
|
this.is_fullscreen = true;
|
|
638
829
|
this.is_minimized = false;
|
|
639
830
|
win.style.position = 'fixed';
|
|
@@ -645,19 +836,19 @@
|
|
|
645
836
|
win.style.zIndex = '99999';
|
|
646
837
|
host.style.width = '100vw';
|
|
647
838
|
const btn = this.querySelector('#tp-maximize-btn');
|
|
648
|
-
if (btn) btn.innerHTML = icon('restore',
|
|
839
|
+
if (btn) btn.innerHTML = icon('restore', 14);
|
|
649
840
|
}
|
|
650
841
|
|
|
651
842
|
exitFullscreen(restore_height = true) {
|
|
652
|
-
const win = this.querySelector('#tp-window')
|
|
653
|
-
|
|
654
|
-
|
|
843
|
+
const win = this.querySelector('#tp-window'),
|
|
844
|
+
position = getClientTheme(this.client_id).position || 'right',
|
|
845
|
+
host = this.querySelector('.tp-chatbot-host');
|
|
655
846
|
this.is_fullscreen = false;
|
|
656
847
|
win.style.position = 'absolute';
|
|
657
848
|
win.style.top = '';
|
|
658
849
|
win.style.left = '0';
|
|
659
850
|
win.style.width = WINDOW_WIDTH;
|
|
660
|
-
win.style.borderRadius = '
|
|
851
|
+
win.style.borderRadius = '20px';
|
|
661
852
|
win.style.zIndex = '';
|
|
662
853
|
host.style.width = WINDOW_WIDTH;
|
|
663
854
|
host.style.left = position === 'left' ? '24px' : 'auto';
|
|
@@ -665,33 +856,33 @@
|
|
|
665
856
|
if (restore_height) win.style.height = WINDOW_HEIGHT;
|
|
666
857
|
win.style.overflow = 'hidden';
|
|
667
858
|
const btn = this.querySelector('#tp-maximize-btn');
|
|
668
|
-
if (btn) btn.innerHTML = icon('restore',
|
|
859
|
+
if (btn) btn.innerHTML = icon('restore', 14);
|
|
669
860
|
}
|
|
670
861
|
|
|
671
|
-
// ─── Events ────────────────────────────────────────────────────────────────
|
|
672
|
-
|
|
673
862
|
attachEvents() {
|
|
674
863
|
this.querySelector('#tp-bubble').addEventListener('click', () => this.toggleChat());
|
|
864
|
+
this.querySelector('#tp-close-win-btn').addEventListener('click', () => {
|
|
865
|
+
if (this.is_open) this.toggleChat();
|
|
866
|
+
});
|
|
675
867
|
this.querySelector('#tp-send').addEventListener('click', () => this.sendMessage());
|
|
676
868
|
this.querySelector('#tp-agent-btn').addEventListener('click', () => this.requestAgent());
|
|
677
|
-
this.querySelector('#tp-back-btn').addEventListener('click', () =>
|
|
678
|
-
|
|
869
|
+
this.querySelector('#tp-back-btn').addEventListener('click', () => {
|
|
870
|
+
this.setTab('new');
|
|
871
|
+
this.showConversationList();
|
|
872
|
+
});
|
|
679
873
|
this.querySelector('#tp-input').addEventListener('keydown', e => {
|
|
680
874
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
681
875
|
e.preventDefault();
|
|
682
876
|
this.sendMessage();
|
|
683
877
|
}
|
|
684
878
|
});
|
|
685
|
-
|
|
686
879
|
this.querySelector('#tp-sound-btn').addEventListener('click', () => {
|
|
687
880
|
this.sound_enabled = !this.sound_enabled;
|
|
688
|
-
this.querySelector('#tp-sound-btn').innerHTML = icon(this.sound_enabled ? 'bell' : 'bell_off',
|
|
881
|
+
this.querySelector('#tp-sound-btn').innerHTML = icon(this.sound_enabled ? 'bell' : 'bell_off', 14);
|
|
689
882
|
});
|
|
690
|
-
|
|
691
883
|
this.querySelector('#tp-close-btn').addEventListener('click', () => {
|
|
692
884
|
if (window.confirm(this.getMessages().close_confirm)) this.closeByUser();
|
|
693
885
|
});
|
|
694
|
-
|
|
695
886
|
this.querySelector('#tp-minimize-btn').addEventListener('click', () => {
|
|
696
887
|
if (this.is_minimized) {
|
|
697
888
|
this.is_minimized = false;
|
|
@@ -702,7 +893,6 @@
|
|
|
702
893
|
this.minimize();
|
|
703
894
|
}
|
|
704
895
|
});
|
|
705
|
-
|
|
706
896
|
this.querySelector('#tp-maximize-btn').addEventListener('click', () => {
|
|
707
897
|
if (this.is_fullscreen) {
|
|
708
898
|
this.exitFullscreen(true);
|
|
@@ -716,29 +906,45 @@
|
|
|
716
906
|
this.enterFullscreen();
|
|
717
907
|
}
|
|
718
908
|
});
|
|
909
|
+
this.querySelector('#tp-tab-new').addEventListener('click', () => {
|
|
910
|
+
this.setTab('new');
|
|
911
|
+
this.showConversationList();
|
|
912
|
+
});
|
|
913
|
+
this.querySelector('#tp-tab-history').addEventListener('click', () => {
|
|
914
|
+
this.setTab('history');
|
|
915
|
+
this.showConversationList();
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
setTab(tab) {
|
|
920
|
+
this.active_tab = tab;
|
|
921
|
+
this.querySelectorAll('.tp-tab').forEach(t => t.classList.toggle('active', t.dataset.tab === tab));
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
showTabs(visible) {
|
|
925
|
+
const tabs = this.querySelector('#tp-tabs');
|
|
926
|
+
if (tabs) tabs.style.display = visible ? 'flex' : 'none';
|
|
719
927
|
}
|
|
720
928
|
|
|
721
929
|
updateUILanguage() {
|
|
722
930
|
const m = this.getMessages();
|
|
723
931
|
const agent_btn = this.querySelector('#tp-agent-btn');
|
|
724
932
|
if (agent_btn)
|
|
725
|
-
agent_btn.innerHTML = `<span style="display:inline-flex;align-items:center;gap:6px;">${icon('agent',
|
|
933
|
+
agent_btn.innerHTML = `<span style="display:inline-flex;align-items:center;gap:6px;">${icon('agent', 15)} ${m.speak_agent}</span>`;
|
|
726
934
|
const close_btn = this.querySelector('#tp-close-btn');
|
|
727
935
|
if (close_btn)
|
|
728
|
-
close_btn.innerHTML = `<span style="display:inline-flex;align-items:center;gap:
|
|
936
|
+
close_btn.innerHTML = `<span style="display:inline-flex;align-items:center;gap:5px;">${icon('check', 12)} ${m.close_btn}</span>`;
|
|
729
937
|
const input = this.querySelector('#tp-input');
|
|
730
938
|
if (input) input.placeholder = m.write_msg;
|
|
731
|
-
const subtitle = this.querySelector('#tp-subtitle');
|
|
732
|
-
if (subtitle) subtitle.innerHTML = `${icon('bot', 12, 'margin-right:4px')} ${m.virtual}`;
|
|
733
939
|
}
|
|
734
940
|
|
|
735
941
|
toggleChat() {
|
|
736
942
|
this.is_open = !this.is_open;
|
|
737
|
-
const window_el = this.querySelector('#tp-window')
|
|
738
|
-
|
|
943
|
+
const window_el = this.querySelector('#tp-window'),
|
|
944
|
+
bubble = this.querySelector('#tp-bubble');
|
|
739
945
|
if (this.is_open) {
|
|
740
946
|
window_el.classList.add('open');
|
|
741
|
-
bubble.innerHTML = icon('close',
|
|
947
|
+
bubble.innerHTML = icon('close', 22, 'color:white');
|
|
742
948
|
if (this.is_minimized) {
|
|
743
949
|
this.is_minimized = false;
|
|
744
950
|
window_el.style.height = WINDOW_HEIGHT;
|
|
@@ -746,7 +952,7 @@
|
|
|
746
952
|
}
|
|
747
953
|
} else {
|
|
748
954
|
window_el.classList.remove('open');
|
|
749
|
-
bubble.innerHTML = icon('msg',
|
|
955
|
+
bubble.innerHTML = icon('msg', 22, 'color:white');
|
|
750
956
|
if (this.is_fullscreen) this.exitFullscreen(true);
|
|
751
957
|
}
|
|
752
958
|
}
|
|
@@ -768,31 +974,20 @@
|
|
|
768
974
|
return data.result?.conversation_id;
|
|
769
975
|
}
|
|
770
976
|
|
|
771
|
-
// ─── Suggestions ──────────────────────────────────────────────────────────
|
|
772
|
-
|
|
773
977
|
showSuggestions(suggestions) {
|
|
774
978
|
const existing = this.querySelector('#tp-suggestions');
|
|
775
979
|
if (existing) existing.remove();
|
|
776
980
|
if (!suggestions || suggestions.length === 0) return;
|
|
777
981
|
const lang = (this.user_info?.language || 'EN').toUpperCase();
|
|
778
|
-
const color = this.getThemeColor();
|
|
779
982
|
const container = this.querySelector('#tp-messages');
|
|
780
983
|
const wrap = document.createElement('div');
|
|
781
984
|
wrap.id = 'tp-suggestions';
|
|
782
|
-
wrap.
|
|
985
|
+
wrap.className = 'tp-suggestions';
|
|
783
986
|
suggestions.forEach(s => {
|
|
784
987
|
const label = s[lang] || s['EN'];
|
|
785
988
|
const btn = document.createElement('button');
|
|
786
|
-
btn.
|
|
989
|
+
btn.className = 'tp-suggestion-btn';
|
|
787
990
|
btn.textContent = `→ ${label}`;
|
|
788
|
-
btn.addEventListener('mouseenter', () => {
|
|
789
|
-
btn.style.background = color;
|
|
790
|
-
btn.style.color = 'white';
|
|
791
|
-
});
|
|
792
|
-
btn.addEventListener('mouseleave', () => {
|
|
793
|
-
btn.style.background = 'white';
|
|
794
|
-
btn.style.color = color;
|
|
795
|
-
});
|
|
796
991
|
btn.addEventListener('click', () => {
|
|
797
992
|
const sugg_el = this.querySelector('#tp-suggestions');
|
|
798
993
|
if (sugg_el) sugg_el.remove();
|
|
@@ -808,8 +1003,6 @@
|
|
|
808
1003
|
container.scrollTop = container.scrollHeight;
|
|
809
1004
|
}
|
|
810
1005
|
|
|
811
|
-
// ─── Conversation List ─────────────────────────────────────────────────────
|
|
812
|
-
|
|
813
1006
|
async showConversationList() {
|
|
814
1007
|
this.view = 'list';
|
|
815
1008
|
const m = this.getMessages();
|
|
@@ -817,94 +1010,107 @@
|
|
|
817
1010
|
clearInterval(this.polling_interval);
|
|
818
1011
|
this.polling_interval = null;
|
|
819
1012
|
}
|
|
820
|
-
|
|
821
1013
|
const subtitle = this.querySelector('#tp-subtitle');
|
|
822
|
-
if (subtitle) subtitle.innerHTML = `${icon('bot',
|
|
1014
|
+
if (subtitle) subtitle.innerHTML = `${icon('bot', 11)} ${m.virtual}`;
|
|
823
1015
|
this.querySelector('#tp-back-btn').style.display = 'none';
|
|
824
|
-
this.querySelector('#tp-
|
|
1016
|
+
this.querySelector('#tp-input-bar').style.display = 'none';
|
|
825
1017
|
this.querySelector('#tp-agent-bar').style.display = 'none';
|
|
826
1018
|
this.querySelector('#tp-close-bar').style.display = 'none';
|
|
827
|
-
|
|
1019
|
+
this.showTabs(true);
|
|
828
1020
|
const container = this.querySelector('#tp-messages');
|
|
829
|
-
container.
|
|
830
|
-
container.innerHTML = `<div style="padding:16px;text-align:center;color:#aaa;font-size:12px;">${m.loading}</div>`;
|
|
831
|
-
|
|
1021
|
+
container.innerHTML = `<div style="padding:24px 16px;text-align:center;color:#ccc;font-size:12px;">${m.loading}</div>`;
|
|
832
1022
|
try {
|
|
833
1023
|
const url = `${this.api_url}/chat/conversations?user_id=${encodeURIComponent(this.user_id)}&limit=5`;
|
|
834
1024
|
const response = await fetch(url, { headers: this.getHeaders() });
|
|
835
1025
|
const data = await response.json();
|
|
836
1026
|
const conversations = data.result?.conversations || [];
|
|
837
1027
|
const color = this.getThemeColor();
|
|
838
|
-
|
|
839
1028
|
container.innerHTML = '';
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
empty.innerHTML = `<div style="margin-bottom:8px;opacity:0.4;">${icon('msg', 32)}</div>${m.no_conv}`;
|
|
856
|
-
container.appendChild(empty);
|
|
857
|
-
} else {
|
|
858
|
-
conversations.forEach(conv => {
|
|
859
|
-
const item = document.createElement('div');
|
|
860
|
-
item.className = 'tp-conv-item';
|
|
861
|
-
item.style.cssText =
|
|
862
|
-
'padding:14px 16px;border-bottom:1px solid #f5f5f5;cursor:pointer;transition:background 0.15s;display:flex;justify-content:space-between;align-items:center;';
|
|
863
|
-
|
|
864
|
-
const is_closed = conv.status === 'closed';
|
|
1029
|
+
if (this.active_tab === 'new') {
|
|
1030
|
+
const greeting = document.createElement('div');
|
|
1031
|
+
greeting.style.cssText = 'padding:20px 16px 8px;';
|
|
1032
|
+
greeting.innerHTML = `<div style="background:white;border-radius:16px;padding:16px;box-shadow:0 1px 8px rgba(0,0,0,0.07);margin-bottom:14px;"><div style="font-size:14px;font-weight:700;color:#1a1a2e;margin-bottom:4px;">${m.hello} ${this.user_info?.first_name || ''} 👋</div><div style="font-size:12px;color:#aaa;line-height:1.5;">${m.recent_conv}</div></div>`;
|
|
1033
|
+
container.appendChild(greeting);
|
|
1034
|
+
if (conversations.length === 0) {
|
|
1035
|
+
const empty = document.createElement('div');
|
|
1036
|
+
empty.style.cssText =
|
|
1037
|
+
'padding:20px 16px;text-align:center;color:#ccc;font-size:13px;display:flex;flex-direction:column;align-items:center;gap:10px;';
|
|
1038
|
+
empty.innerHTML = `<div style="opacity:0.35;">${icon('msg', 36)}</div>${m.no_conv}`;
|
|
1039
|
+
container.appendChild(empty);
|
|
1040
|
+
} else {
|
|
1041
|
+
const last_conv = conversations[0];
|
|
1042
|
+
const is_closed = last_conv.status === 'closed';
|
|
1043
|
+
const badge_color = last_conv.status === 'waiting_agent' ? '#f59e0b' : color;
|
|
865
1044
|
const status_label = is_closed
|
|
866
1045
|
? m.status_closed
|
|
867
|
-
:
|
|
1046
|
+
: last_conv.status === 'agent'
|
|
868
1047
|
? m.status_agent
|
|
869
|
-
:
|
|
1048
|
+
: last_conv.status === 'waiting_agent'
|
|
870
1049
|
? m.status_waiting
|
|
871
1050
|
: m.status_bot;
|
|
872
|
-
|
|
873
|
-
const badge_color = is_closed ? color : conv.status === 'waiting_agent' ? '#f59e0b' : color;
|
|
874
|
-
const date = new Date(conv.updated_at).toLocaleDateString('fr-FR', {
|
|
1051
|
+
const date = new Date(last_conv.updated_at).toLocaleDateString('fr-FR', {
|
|
875
1052
|
day: '2-digit',
|
|
876
1053
|
month: '2-digit',
|
|
877
1054
|
hour: '2-digit',
|
|
878
1055
|
minute: '2-digit',
|
|
879
1056
|
});
|
|
880
|
-
|
|
881
|
-
item.
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
item.addEventListener('click', () => this.openConversation(
|
|
1057
|
+
const item = document.createElement('div');
|
|
1058
|
+
item.style.cssText =
|
|
1059
|
+
'margin:0 16px 14px;background:white;border-radius:14px;padding:14px 16px;box-shadow:0 1px 6px rgba(0,0,0,0.07);cursor:pointer;transition:box-shadow 0.15s;display:flex;justify-content:space-between;align-items:center;';
|
|
1060
|
+
item.innerHTML = `<div><div style="font-size:12px;font-weight:600;color:${is_closed ? '#aaa' : '#1a1a2e'};margin-bottom:3px;">${date}</div><div style="font-size:11px;color:#ccc;">${last_conv.message_count} ${m.messages}${last_conv.message_count > 1 ? 's' : ''}</div></div><span style="font-size:10px;font-weight:700;padding:3px 10px;border-radius:20px;background:${badge_color}18;color:${badge_color};">${status_label}</span>`;
|
|
1061
|
+
item.addEventListener('mouseenter', () => {
|
|
1062
|
+
item.style.boxShadow = '0 3px 12px rgba(0,0,0,0.12)';
|
|
1063
|
+
});
|
|
1064
|
+
item.addEventListener('mouseleave', () => {
|
|
1065
|
+
item.style.boxShadow = '0 1px 6px rgba(0,0,0,0.07)';
|
|
1066
|
+
});
|
|
1067
|
+
item.addEventListener('click', () => this.openConversation(last_conv.conversation_id, is_closed));
|
|
891
1068
|
container.appendChild(item);
|
|
892
|
-
}
|
|
1069
|
+
}
|
|
1070
|
+
const btn_wrap = document.createElement('div');
|
|
1071
|
+
btn_wrap.style.cssText = 'padding:4px 16px 16px;';
|
|
1072
|
+
btn_wrap.innerHTML = `<button class="tp-new-conv-btn">${icon('msg_plus', 15, 'color:white')} ${m.new_conv_btn}</button>`;
|
|
1073
|
+
btn_wrap.querySelector('button').addEventListener('click', () => this.startNewConversation());
|
|
1074
|
+
container.appendChild(btn_wrap);
|
|
1075
|
+
} else {
|
|
1076
|
+
const greeting = document.createElement('div');
|
|
1077
|
+
greeting.style.cssText = 'padding:16px 16px 8px;';
|
|
1078
|
+
greeting.innerHTML = `<div style="font-size:12px;font-weight:600;color:#aaa;text-transform:uppercase;letter-spacing:0.5px;">${m.recent_conv}</div>`;
|
|
1079
|
+
container.appendChild(greeting);
|
|
1080
|
+
if (conversations.length === 0) {
|
|
1081
|
+
const empty = document.createElement('div');
|
|
1082
|
+
empty.style.cssText =
|
|
1083
|
+
'padding:32px 16px;text-align:center;color:#ccc;font-size:13px;display:flex;flex-direction:column;align-items:center;gap:10px;';
|
|
1084
|
+
empty.innerHTML = `<div style="opacity:0.35;">${icon('history', 36)}</div>${m.no_conv}`;
|
|
1085
|
+
container.appendChild(empty);
|
|
1086
|
+
} else {
|
|
1087
|
+
conversations.forEach(conv => {
|
|
1088
|
+
const is_closed = conv.status === 'closed';
|
|
1089
|
+
const badge_color = conv.status === 'waiting_agent' ? '#f59e0b' : color;
|
|
1090
|
+
const status_label = is_closed
|
|
1091
|
+
? m.status_closed
|
|
1092
|
+
: conv.status === 'agent'
|
|
1093
|
+
? m.status_agent
|
|
1094
|
+
: conv.status === 'waiting_agent'
|
|
1095
|
+
? m.status_waiting
|
|
1096
|
+
: m.status_bot;
|
|
1097
|
+
const date = new Date(conv.updated_at).toLocaleDateString('fr-FR', {
|
|
1098
|
+
day: '2-digit',
|
|
1099
|
+
month: '2-digit',
|
|
1100
|
+
hour: '2-digit',
|
|
1101
|
+
minute: '2-digit',
|
|
1102
|
+
});
|
|
1103
|
+
const item = document.createElement('div');
|
|
1104
|
+
item.className = 'tp-conv-item';
|
|
1105
|
+
item.innerHTML = `<div class="tp-conv-item-left"><div class="tp-conv-item-date" style="color:${is_closed ? '#aaa' : '#1a1a2e'}">${date}</div><div class="tp-conv-item-count">${conv.message_count} ${m.messages}${conv.message_count > 1 ? 's' : ''}</div></div><span class="tp-badge" style="background:${badge_color}18;color:${badge_color};">${is_closed ? icon('check', 10) : ''} ${status_label}</span>`;
|
|
1106
|
+
item.addEventListener('click', () => this.openConversation(conv.conversation_id, is_closed));
|
|
1107
|
+
container.appendChild(item);
|
|
1108
|
+
});
|
|
1109
|
+
}
|
|
893
1110
|
}
|
|
894
|
-
|
|
895
|
-
// New conversation button
|
|
896
|
-
const new_btn_wrap = document.createElement('div');
|
|
897
|
-
new_btn_wrap.style.cssText = 'padding:16px;';
|
|
898
|
-
new_btn_wrap.innerHTML = `
|
|
899
|
-
<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;display:flex;align-items:center;justify-content:center;gap:8px;">
|
|
900
|
-
${icon('msg_plus', 16, 'color:white')} ${m.new_conv_btn}
|
|
901
|
-
</button>
|
|
902
|
-
`;
|
|
903
|
-
new_btn_wrap.querySelector('button').addEventListener('click', () => this.startNewConversation());
|
|
904
|
-
container.appendChild(new_btn_wrap);
|
|
905
1111
|
} catch (e) {
|
|
906
1112
|
console.error('showConversationList error:', e);
|
|
907
|
-
container.innerHTML = `<div style="padding:16px;text-align:center;color:#ef4444;font-size:13px;">${
|
|
1113
|
+
container.innerHTML = `<div style="padding:24px 16px;text-align:center;color:#ef4444;font-size:13px;">${this.getMessages().error_load}</div>`;
|
|
908
1114
|
}
|
|
909
1115
|
}
|
|
910
1116
|
|
|
@@ -916,56 +1122,42 @@
|
|
|
916
1122
|
this.is_closed = is_closed;
|
|
917
1123
|
this.agent_mode = false;
|
|
918
1124
|
this.agent_requested = false;
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
this.querySelector('#tp-back-btn').style.display = 'block';
|
|
1125
|
+
this.querySelector('#tp-back-btn').style.display = 'flex';
|
|
1126
|
+
this.showTabs(false);
|
|
922
1127
|
const container = this.querySelector('#tp-messages');
|
|
923
|
-
container.style.background = '#f5f5f7';
|
|
924
|
-
container.style.padding = '16px';
|
|
925
1128
|
container.innerHTML = '';
|
|
926
|
-
|
|
927
|
-
const input_bar = this.querySelector('#tp-chatbot-input-bar');
|
|
928
|
-
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
929
|
-
const close_bar = this.querySelector('#tp-close-bar');
|
|
930
|
-
|
|
931
1129
|
try {
|
|
932
1130
|
const url = `${this.api_url}/chat/conversations/${conversation_id}?user_id=${encodeURIComponent(this.user_id)}`;
|
|
933
1131
|
const response = await fetch(url, { headers: this.getHeaders() });
|
|
934
1132
|
const data = await response.json();
|
|
935
1133
|
const conv = data.result?.conversation;
|
|
936
1134
|
if (!conv) return;
|
|
937
|
-
|
|
938
1135
|
conv.messages.forEach(msg => {
|
|
939
|
-
if (['user', 'assistant', 'agent', 'system'].includes(msg.role))
|
|
940
|
-
this.addMessage(msg.role, msg.content, msg.message_id || null);
|
|
941
|
-
}
|
|
1136
|
+
if (['user', 'assistant', 'agent', 'system'].includes(msg.role)) this.addMessage(msg.role, msg.content, msg.message_id || null);
|
|
942
1137
|
});
|
|
943
1138
|
this.last_message_count = conv.messages.length;
|
|
944
|
-
|
|
945
1139
|
if (conv.status === 'closed') {
|
|
946
1140
|
this.showClosed(conv.csat || null);
|
|
947
1141
|
return;
|
|
948
1142
|
}
|
|
949
|
-
|
|
1143
|
+
const input_bar = this.querySelector('#tp-input-bar'),
|
|
1144
|
+
agent_bar = this.querySelector('#tp-agent-bar'),
|
|
1145
|
+
close_bar = this.querySelector('#tp-close-bar');
|
|
950
1146
|
if (conv.status === 'bot' || conv.status === 'waiting_agent') {
|
|
951
1147
|
if (close_bar) close_bar.style.display = 'block';
|
|
952
1148
|
} else {
|
|
953
1149
|
if (close_bar) close_bar.style.display = 'none';
|
|
954
1150
|
}
|
|
955
|
-
|
|
956
1151
|
if (conv.status === 'agent') {
|
|
957
1152
|
this.agent_mode = true;
|
|
958
|
-
|
|
959
|
-
if (subtitle) subtitle.innerHTML = `${icon('user', 12, 'margin-right:4px')} ${m.human_agent}`;
|
|
1153
|
+
this._updateSubtitle('agent');
|
|
960
1154
|
if (agent_bar) agent_bar.style.display = 'none';
|
|
961
1155
|
if (input_bar) input_bar.style.display = 'flex';
|
|
962
1156
|
} else {
|
|
963
1157
|
if (input_bar) input_bar.style.display = 'flex';
|
|
964
1158
|
if (agent_bar) agent_bar.style.display = 'none';
|
|
965
|
-
|
|
966
|
-
if (subtitle) subtitle.innerHTML = `${icon('bot', 12, 'margin-right:4px')} ${m.virtual}`;
|
|
1159
|
+
this._updateSubtitle('bot');
|
|
967
1160
|
}
|
|
968
|
-
|
|
969
1161
|
this.updateUILanguage();
|
|
970
1162
|
this.polling_interval = setInterval(() => this.pollMessages(), 3000);
|
|
971
1163
|
} catch (e) {
|
|
@@ -973,6 +1165,7 @@
|
|
|
973
1165
|
}
|
|
974
1166
|
}
|
|
975
1167
|
|
|
1168
|
+
// ─── FIXED: conv créée au premier sendMessage, pas à l'ouverture ──────────
|
|
976
1169
|
async startNewConversation() {
|
|
977
1170
|
this.view = 'chat';
|
|
978
1171
|
this.is_closed = false;
|
|
@@ -980,71 +1173,78 @@
|
|
|
980
1173
|
this.agent_requested = false;
|
|
981
1174
|
this.messages = [];
|
|
982
1175
|
this.last_message_count = 0;
|
|
983
|
-
this.conversation_id = null;
|
|
984
|
-
|
|
985
|
-
const m = this.getMessages();
|
|
986
|
-
this.querySelector('#tp-back-btn').style.display = 'block';
|
|
987
|
-
this.querySelector('#tp-close-bar').style.display = 'block';
|
|
1176
|
+
this.conversation_id = null; // pas encore créée
|
|
988
1177
|
|
|
1178
|
+
this.querySelector('#tp-back-btn').style.display = 'flex';
|
|
1179
|
+
this.querySelector('#tp-close-bar').style.display = 'none'; // cachée tant que pas de conv
|
|
1180
|
+
this.showTabs(false);
|
|
989
1181
|
const container = this.querySelector('#tp-messages');
|
|
990
|
-
container.style.background = '#f5f5f7';
|
|
991
|
-
container.style.padding = '16px';
|
|
992
1182
|
container.innerHTML = '';
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
if (subtitle) subtitle.innerHTML = `${icon('bot', 12, 'margin-right:4px')} ${m.virtual}`;
|
|
996
|
-
|
|
997
|
-
this.querySelector('#tp-chatbot-input-bar').style.display = 'flex';
|
|
1183
|
+
this._updateSubtitle('bot');
|
|
1184
|
+
this.querySelector('#tp-input-bar').style.display = 'flex';
|
|
998
1185
|
this.querySelector('#tp-agent-bar').style.display = 'none';
|
|
999
1186
|
this.updateUILanguage();
|
|
1000
|
-
|
|
1001
|
-
this.conversation_id = await this.createConversation();
|
|
1002
1187
|
this.addMessage('assistant', this.getWelcomeMessage(this.user_info?.first_name || ''));
|
|
1003
|
-
this.last_message_count = 1;
|
|
1004
|
-
|
|
1005
1188
|
const theme = getClientTheme(this.client_id);
|
|
1006
1189
|
if (theme.suggestions && theme.suggestions.length > 0) this.showSuggestions(theme.suggestions);
|
|
1007
|
-
|
|
1008
|
-
this.polling_interval = setInterval(() => this.pollMessages(), 3000);
|
|
1190
|
+
// pas de polling — rien à poller tant que la conv n'est pas créée
|
|
1009
1191
|
}
|
|
1010
1192
|
|
|
1011
|
-
|
|
1193
|
+
_updateSubtitle(mode) {
|
|
1194
|
+
const m = this.getMessages(),
|
|
1195
|
+
subtitle = this.querySelector('#tp-subtitle');
|
|
1196
|
+
if (!subtitle) return;
|
|
1197
|
+
if (mode === 'agent') subtitle.innerHTML = `${icon('user', 11)} ${m.human_agent}`;
|
|
1198
|
+
else if (mode === 'closed') subtitle.innerHTML = `${icon('check', 11)} ${m.terminated}`;
|
|
1199
|
+
else subtitle.innerHTML = `${icon('bot', 11)} ${m.virtual}`;
|
|
1200
|
+
}
|
|
1012
1201
|
|
|
1013
1202
|
showGuestForm() {
|
|
1014
1203
|
const container = this.querySelector('#tp-messages');
|
|
1015
1204
|
if (!container) return;
|
|
1016
|
-
this.querySelector('#tp-
|
|
1205
|
+
this.querySelector('#tp-input-bar').style.display = 'none';
|
|
1017
1206
|
this.querySelector('#tp-agent-bar').style.display = 'none';
|
|
1018
|
-
|
|
1019
|
-
const
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
<
|
|
1027
|
-
<
|
|
1028
|
-
<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;">Start conversation →</button>
|
|
1029
|
-
<p id="tp-guest-error" style="margin:0;font-size:12px;color:#c0392b;display:none;"></p>
|
|
1207
|
+
this.showTabs(false);
|
|
1208
|
+
const m = this.getMessages();
|
|
1209
|
+
container.innerHTML = '';
|
|
1210
|
+
const wrap = document.createElement('div');
|
|
1211
|
+
wrap.id = 'tp-guest-wrap';
|
|
1212
|
+
wrap.className = 'tp-guest-wrap';
|
|
1213
|
+
wrap.innerHTML = `
|
|
1214
|
+
<div class="tp-guest-intro">
|
|
1215
|
+
<div class="tp-guest-intro-name">${m.guest_title}</div>
|
|
1216
|
+
<div class="tp-guest-intro-sub">${m.guest_sub}</div>
|
|
1030
1217
|
</div>
|
|
1218
|
+
<div><label class="tp-guest-field-label">${m.guest_firstname}</label><input id="tp-guest-firstname" type="text" class="tp-guest-input" placeholder="Jean" /></div>
|
|
1219
|
+
<div><label class="tp-guest-field-label">${m.guest_company}</label><input id="tp-guest-company" type="text" class="tp-guest-input" placeholder="Acme Corp" /></div>
|
|
1220
|
+
<div><label class="tp-guest-field-label">${m.guest_email}</label><input id="tp-guest-email" type="email" class="tp-guest-input" placeholder="jean@acme.com" /></div>
|
|
1221
|
+
<p id="tp-guest-error" class="tp-guest-error"></p>
|
|
1222
|
+
<button id="tp-guest-submit" class="tp-guest-submit">${icon('msg_plus', 15, 'color:white')} ${m.guest_submit}</button>
|
|
1223
|
+
<div class="tp-guest-footer">${icon('lock', 11)} ${m.guest_secured}</div>
|
|
1031
1224
|
`;
|
|
1032
|
-
container.appendChild(
|
|
1225
|
+
container.appendChild(wrap);
|
|
1033
1226
|
this.querySelector('#tp-guest-submit').addEventListener('click', () => this.submitGuestForm());
|
|
1227
|
+
['#tp-guest-firstname', '#tp-guest-company', '#tp-guest-email'].forEach(sel => {
|
|
1228
|
+
this.querySelector(sel)?.addEventListener('keydown', e => {
|
|
1229
|
+
if (e.key === 'Enter') this.submitGuestForm();
|
|
1230
|
+
});
|
|
1231
|
+
});
|
|
1034
1232
|
}
|
|
1035
1233
|
|
|
1234
|
+
// ─── FIXED: conv créée au premier sendMessage, pas après le formulaire ────
|
|
1036
1235
|
async submitGuestForm() {
|
|
1037
1236
|
const first_name = this.querySelector('#tp-guest-firstname')?.value.trim();
|
|
1038
1237
|
const company_name = this.querySelector('#tp-guest-company')?.value.trim();
|
|
1039
1238
|
const email = this.querySelector('#tp-guest-email')?.value.trim();
|
|
1040
1239
|
const error_el = this.querySelector('#tp-guest-error');
|
|
1240
|
+
const m = this.getMessages();
|
|
1041
1241
|
if (!first_name || !company_name || !email) {
|
|
1042
|
-
error_el.textContent =
|
|
1242
|
+
error_el.textContent = m.guest_fill;
|
|
1043
1243
|
error_el.style.display = 'block';
|
|
1044
1244
|
return;
|
|
1045
1245
|
}
|
|
1046
1246
|
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
1047
|
-
error_el.textContent =
|
|
1247
|
+
error_el.textContent = m.guest_email_invalid;
|
|
1048
1248
|
error_el.style.display = 'block';
|
|
1049
1249
|
return;
|
|
1050
1250
|
}
|
|
@@ -1052,66 +1252,49 @@
|
|
|
1052
1252
|
this.user_info = { user_id: `guest-${email}`, first_name, last_name: '', company_name, email, language: 'EN', site_id: '' };
|
|
1053
1253
|
this.user_id = this.user_info.user_id;
|
|
1054
1254
|
|
|
1055
|
-
const
|
|
1056
|
-
if (
|
|
1255
|
+
const wrap = this.querySelector('#tp-guest-wrap');
|
|
1256
|
+
if (wrap) wrap.remove();
|
|
1057
1257
|
|
|
1058
1258
|
this.view = 'chat';
|
|
1059
|
-
this.conversation_id =
|
|
1060
|
-
|
|
1061
|
-
this.querySelector('#tp-chatbot-input-bar').style.display = 'flex';
|
|
1259
|
+
this.conversation_id = null; // pas encore créée — sera créée au premier sendMessage
|
|
1260
|
+
this.querySelector('#tp-input-bar').style.display = 'flex';
|
|
1062
1261
|
this.querySelector('#tp-agent-bar').style.display = 'none';
|
|
1063
|
-
this.querySelector('#tp-close-bar').style.display = '
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
container.style.background = '#f5f5f7';
|
|
1067
|
-
container.style.padding = '16px';
|
|
1068
|
-
|
|
1262
|
+
this.querySelector('#tp-close-bar').style.display = 'none'; // cachée tant que pas de conv
|
|
1263
|
+
this.querySelector('#tp-back-btn').style.display = 'flex';
|
|
1264
|
+
this.showTabs(false);
|
|
1069
1265
|
this.updateUILanguage();
|
|
1070
1266
|
this.addMessage('assistant', this.getWelcomeMessage(first_name));
|
|
1071
|
-
this.last_message_count = 1;
|
|
1072
|
-
|
|
1073
1267
|
const theme = getClientTheme(this.client_id);
|
|
1074
1268
|
if (theme.suggestions && theme.suggestions.length > 0) this.showSuggestions(theme.suggestions);
|
|
1075
|
-
|
|
1076
|
-
this.polling_interval = setInterval(() => this.pollMessages(), 3000);
|
|
1269
|
+
// pas de polling — rien à poller tant que la conv n'est pas créée
|
|
1077
1270
|
}
|
|
1078
1271
|
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
addMessage(role, content, message_id = null) {
|
|
1082
|
-
this.messages.push({ role, content, message_id, created_at: new Date().toISOString() });
|
|
1272
|
+
addMessage(role, content, message_id = null, source = null) {
|
|
1273
|
+
this.messages.push({ role, content, message_id, source, created_at: new Date().toISOString() });
|
|
1083
1274
|
const container = this.querySelector('#tp-messages');
|
|
1084
|
-
|
|
1275
|
+
const m = this.getMessages();
|
|
1085
1276
|
const role_label =
|
|
1086
1277
|
role === 'user'
|
|
1087
|
-
? `${icon('user', 11
|
|
1278
|
+
? `${icon('user', 11)} ${this.user_info?.first_name || 'You'}`
|
|
1088
1279
|
: role === 'agent'
|
|
1089
|
-
? `${icon('agent', 11
|
|
1280
|
+
? `${icon('agent', 11)} Agent`
|
|
1090
1281
|
: role === 'system'
|
|
1091
1282
|
? 'Info'
|
|
1092
|
-
: `${icon('bot', 11
|
|
1093
|
-
|
|
1283
|
+
: `${icon('bot', 11)} Maria`;
|
|
1094
1284
|
const msg_el = document.createElement('div');
|
|
1095
|
-
msg_el.className = `tp-
|
|
1285
|
+
msg_el.className = `tp-msg ${role}`;
|
|
1096
1286
|
const rendered_content =
|
|
1097
1287
|
role === 'assistant' || role === 'agent' ? (typeof marked !== 'undefined' ? marked.parse(content) : content) : content;
|
|
1098
|
-
|
|
1288
|
+
const now = formatTime(new Date().toISOString());
|
|
1099
1289
|
const feedback_html =
|
|
1100
1290
|
role === 'assistant' && message_id
|
|
1101
|
-
? `<div class="tp-feedback-bar" data-message-id="${message_id}"
|
|
1102
|
-
<button class="tp-feedback-btn" data-rating="positive" title="Helpful">${icon('helpful', 14)}</button>
|
|
1103
|
-
<button class="tp-feedback-btn" data-rating="negative" title="Not helpful">${icon('not_helpful', 14)}</button>
|
|
1104
|
-
</div>`
|
|
1291
|
+
? `<div class="tp-feedback-bar" data-message-id="${message_id}"><button class="tp-feedback-btn" data-rating="positive" title="${m.helpful}">${icon('helpful', 13)} <span style="font-size:11px;">${m.helpful}</span></button><button class="tp-feedback-btn" data-rating="negative" title="${m.not_helpful}">${icon('not_helpful', 13)} <span style="font-size:11px;">${m.not_helpful}</span></button></div>`
|
|
1105
1292
|
: '';
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
${feedback_html}
|
|
1112
|
-
</div>
|
|
1113
|
-
`;
|
|
1114
|
-
|
|
1293
|
+
if (role === 'system') {
|
|
1294
|
+
msg_el.innerHTML = `<div class="tp-bubble-msg">${rendered_content}</div>`;
|
|
1295
|
+
} else {
|
|
1296
|
+
msg_el.innerHTML = `<div class="tp-msg-role">${role_label}</div><div class="tp-bubble-msg${role === 'assistant' || role === 'agent' ? ' tp-md' : ''}">${rendered_content}${feedback_html}</div><div class="tp-msg-time">${role === 'user' ? `${this.user_info?.first_name || 'You'} · ${now}` : `Maria · ${now}`}</div>`;
|
|
1297
|
+
}
|
|
1115
1298
|
if (role === 'assistant' && message_id) {
|
|
1116
1299
|
const bar = msg_el.querySelector('.tp-feedback-bar');
|
|
1117
1300
|
bar.querySelectorAll('.tp-feedback-btn').forEach(btn => {
|
|
@@ -1120,9 +1303,11 @@
|
|
|
1120
1303
|
bar.dataset.voted = '1';
|
|
1121
1304
|
const rating = btn.dataset.rating;
|
|
1122
1305
|
bar.querySelectorAll('.tp-feedback-btn').forEach(b => {
|
|
1123
|
-
b.
|
|
1306
|
+
b.classList.remove('voted-positive', 'voted-negative');
|
|
1307
|
+
b.style.opacity = b === btn ? '1' : '0.35';
|
|
1124
1308
|
b.style.cursor = 'default';
|
|
1125
1309
|
});
|
|
1310
|
+
btn.classList.add(rating === 'positive' ? 'voted-positive' : 'voted-negative');
|
|
1126
1311
|
try {
|
|
1127
1312
|
await fetch(`${this.api_url}/chat/conversations/${this.conversation_id}/feedback`, {
|
|
1128
1313
|
method: 'POST',
|
|
@@ -1133,19 +1318,17 @@
|
|
|
1133
1318
|
});
|
|
1134
1319
|
});
|
|
1135
1320
|
}
|
|
1136
|
-
|
|
1137
1321
|
container.appendChild(msg_el);
|
|
1138
1322
|
container.scrollTop = container.scrollHeight;
|
|
1139
|
-
|
|
1140
1323
|
if ((role === 'assistant' || role === 'agent') && this.sound_enabled) playSound();
|
|
1141
1324
|
}
|
|
1142
1325
|
|
|
1143
1326
|
showTyping() {
|
|
1144
1327
|
const container = this.querySelector('#tp-messages');
|
|
1145
1328
|
const typing = document.createElement('div');
|
|
1146
|
-
typing.className = 'tp-
|
|
1329
|
+
typing.className = 'tp-msg assistant';
|
|
1147
1330
|
typing.id = 'tp-typing';
|
|
1148
|
-
typing.innerHTML = `<div class="tp-
|
|
1331
|
+
typing.innerHTML = `<div class="tp-msg-role">${icon('bot', 11)} Maria</div><div class="tp-bubble-msg"><div class="tp-typing"><span></span><span></span><span></span></div></div>`;
|
|
1149
1332
|
container.appendChild(typing);
|
|
1150
1333
|
container.scrollTop = container.scrollHeight;
|
|
1151
1334
|
}
|
|
@@ -1161,56 +1344,33 @@
|
|
|
1161
1344
|
clearInterval(this.polling_interval);
|
|
1162
1345
|
this.polling_interval = null;
|
|
1163
1346
|
}
|
|
1164
|
-
|
|
1165
1347
|
const m = this.getMessages();
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
this.querySelector('#tp-chatbot-input-bar').style.display = 'none';
|
|
1348
|
+
this._updateSubtitle('closed');
|
|
1349
|
+
this.querySelector('#tp-input-bar').style.display = 'none';
|
|
1170
1350
|
this.querySelector('#tp-agent-bar').style.display = 'none';
|
|
1171
1351
|
this.querySelector('#tp-close-bar').style.display = 'none';
|
|
1172
1352
|
const sugg_el = this.querySelector('#tp-suggestions');
|
|
1173
1353
|
if (sugg_el) sugg_el.remove();
|
|
1174
|
-
|
|
1175
1354
|
const container = this.querySelector('#tp-messages');
|
|
1176
|
-
const existing_banner =
|
|
1355
|
+
const existing_banner = container.querySelector('#tp-closed-banner');
|
|
1177
1356
|
if (existing_banner) {
|
|
1178
1357
|
if (existing_banner.querySelector('#tp-csat-block')) return;
|
|
1179
1358
|
existing_banner.remove();
|
|
1180
1359
|
}
|
|
1181
|
-
|
|
1182
|
-
const color = this.getThemeColor();
|
|
1183
|
-
const dark = this.shadeColor(color, -20);
|
|
1184
|
-
|
|
1360
|
+
const now = new Date().toLocaleDateString('fr-FR', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' });
|
|
1185
1361
|
const csat_html = existing_csat
|
|
1186
|
-
? `<div
|
|
1187
|
-
: `<div
|
|
1188
|
-
<div style="display:flex;gap:10px;justify-content:center;margin-bottom:14px;">
|
|
1189
|
-
<button id="tp-csat-positive" class="tp-csat-btn" style="border-color:#22c55e;color:#22c55e;">${icon('helpful', 20)}</button>
|
|
1190
|
-
<button id="tp-csat-negative" class="tp-csat-btn" style="border-color:#ef4444;color:#ef4444;">${icon('not_helpful', 20)}</button>
|
|
1191
|
-
</div>`;
|
|
1192
|
-
|
|
1362
|
+
? `<div class="tp-csat-thanks">${existing_csat === 'positive' ? m.csat_positive : m.csat_negative}</div>`
|
|
1363
|
+
: `<div class="tp-csat-q">${m.csat_question}</div><div class="tp-csat-btns"><button id="tp-csat-positive" class="tp-csat-btn positive">${icon('helpful', 16)} ${m.helpful}</button><button id="tp-csat-negative" class="tp-csat-btn negative">${icon('not_helpful', 16)} ${m.not_helpful}</button></div>`;
|
|
1193
1364
|
const banner = document.createElement('div');
|
|
1194
1365
|
banner.id = 'tp-closed-banner';
|
|
1195
|
-
banner.
|
|
1196
|
-
|
|
1197
|
-
<div style="display:inline-flex;align-items:center;gap:6px;font-size:13px;color:#1a1a2e;font-weight:600;margin-bottom:10px;">
|
|
1198
|
-
${icon('check', 14)} ${m.closed}
|
|
1199
|
-
</div>
|
|
1200
|
-
<div id="tp-csat-block">${csat_html}</div>
|
|
1201
|
-
<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:700;cursor:pointer;margin-bottom:8px;width:100%;display:flex;align-items:center;justify-content:center;gap:8px;">
|
|
1202
|
-
${icon('msg_plus', 15, 'color:white')} ${m.new_conv}
|
|
1203
|
-
</button>
|
|
1204
|
-
<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>
|
|
1205
|
-
</div>
|
|
1206
|
-
`;
|
|
1366
|
+
banner.className = 'tp-closed-banner';
|
|
1367
|
+
banner.innerHTML = `<div class="tp-closed-title">${icon('check', 14)} ${m.closed}</div><div class="tp-closed-date">${now}</div><div id="tp-csat-block">${csat_html}</div><button class="tp-closed-new-btn" id="tp-closed-new-btn">${icon('msg_plus', 14, 'color:white')} ${m.new_conv}</button><button class="tp-closed-hist-btn" id="tp-closed-hist-btn">${m.back_to_list}</button>`;
|
|
1207
1368
|
container.appendChild(banner);
|
|
1208
1369
|
container.scrollTop = container.scrollHeight;
|
|
1209
|
-
|
|
1210
1370
|
if (!existing_csat) {
|
|
1211
1371
|
const submit_csat = async rating => {
|
|
1212
|
-
const
|
|
1213
|
-
if (!
|
|
1372
|
+
const block = this.querySelector('#tp-csat-block');
|
|
1373
|
+
if (!block) return;
|
|
1214
1374
|
try {
|
|
1215
1375
|
await fetch(`${this.api_url}/chat/conversations/${this.conversation_id}/csat`, {
|
|
1216
1376
|
method: 'POST',
|
|
@@ -1220,20 +1380,21 @@
|
|
|
1220
1380
|
} catch (e) {
|
|
1221
1381
|
console.error('CSAT error:', e);
|
|
1222
1382
|
}
|
|
1223
|
-
|
|
1383
|
+
block.innerHTML = `<div class="tp-csat-thanks">${rating === 'positive' ? m.csat_positive : m.csat_negative}</div>`;
|
|
1224
1384
|
};
|
|
1225
|
-
this.querySelector('#tp-csat-positive')
|
|
1226
|
-
this.querySelector('#tp-csat-negative')
|
|
1385
|
+
this.querySelector('#tp-csat-positive')?.addEventListener('click', () => submit_csat('positive'));
|
|
1386
|
+
this.querySelector('#tp-csat-negative')?.addEventListener('click', () => submit_csat('negative'));
|
|
1227
1387
|
}
|
|
1228
|
-
|
|
1229
|
-
this.querySelector('#tp-
|
|
1230
|
-
|
|
1388
|
+
this.querySelector('#tp-closed-new-btn').addEventListener('click', () => this.startNewConversation());
|
|
1389
|
+
this.querySelector('#tp-closed-hist-btn').addEventListener('click', () => {
|
|
1390
|
+
this.setTab('history');
|
|
1391
|
+
this.showConversationList();
|
|
1392
|
+
});
|
|
1231
1393
|
}
|
|
1232
1394
|
|
|
1233
|
-
// ───
|
|
1234
|
-
|
|
1395
|
+
// ─── FIXED: createConversation lazy au premier sendMessage ────────────────
|
|
1235
1396
|
async sendMessage() {
|
|
1236
|
-
if (this.is_closed
|
|
1397
|
+
if (this.is_closed) return;
|
|
1237
1398
|
const m = this.getMessages();
|
|
1238
1399
|
const input = this.querySelector('#tp-input');
|
|
1239
1400
|
const query = input.value.trim();
|
|
@@ -1245,10 +1406,22 @@
|
|
|
1245
1406
|
const user_messages = this.messages.filter(msg => msg.role === 'user').length;
|
|
1246
1407
|
if (user_messages >= 30) {
|
|
1247
1408
|
this.addMessage('system', m.limit_reached);
|
|
1248
|
-
this.querySelector('#tp-
|
|
1409
|
+
this.querySelector('#tp-input-bar').style.display = 'none';
|
|
1249
1410
|
return;
|
|
1250
1411
|
}
|
|
1251
1412
|
|
|
1413
|
+
// Créer la conv au premier message si pas encore créée
|
|
1414
|
+
if (!this.conversation_id) {
|
|
1415
|
+
try {
|
|
1416
|
+
this.conversation_id = await this.createConversation();
|
|
1417
|
+
this.querySelector('#tp-close-bar').style.display = 'block';
|
|
1418
|
+
this.polling_interval = setInterval(() => this.pollMessages(), 3000);
|
|
1419
|
+
} catch (e) {
|
|
1420
|
+
this.addMessage('system', m.error_occurred);
|
|
1421
|
+
return;
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1252
1425
|
input.value = '';
|
|
1253
1426
|
this.is_loading = true;
|
|
1254
1427
|
this.addMessage('user', query);
|
|
@@ -1271,14 +1444,10 @@
|
|
|
1271
1444
|
|
|
1272
1445
|
this.agent_mode = data.result.agent_mode;
|
|
1273
1446
|
const source = data.result.source || null;
|
|
1274
|
-
|
|
1275
|
-
if (subtitle)
|
|
1276
|
-
subtitle.innerHTML = this.agent_mode
|
|
1277
|
-
? `${icon('user', 12, 'margin-right:4px')} ${m.human_agent}`
|
|
1278
|
-
: `${icon('bot', 12, 'margin-right:4px')} ${m.virtual}`;
|
|
1447
|
+
this._updateSubtitle(this.agent_mode ? 'agent' : 'bot');
|
|
1279
1448
|
|
|
1280
1449
|
if (data.result.reply) {
|
|
1281
|
-
this.addMessage(this.agent_mode ? 'agent' : 'assistant', data.result.reply, data.result.message_id || null);
|
|
1450
|
+
this.addMessage(this.agent_mode ? 'agent' : 'assistant', data.result.reply, data.result.message_id || null, source);
|
|
1282
1451
|
this.last_message_count += 2;
|
|
1283
1452
|
}
|
|
1284
1453
|
|
|
@@ -1287,18 +1456,11 @@
|
|
|
1287
1456
|
const suggest_agent = data.result.suggest_agent || false;
|
|
1288
1457
|
|
|
1289
1458
|
if (agent_bar && !this.agent_requested && !this.agent_mode) {
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
source === 'no_match' ||
|
|
1295
|
-
source === 'out_of_scope' ||
|
|
1296
|
-
user_msg_count >= 5
|
|
1297
|
-
) {
|
|
1298
|
-
agent_bar.style.display = 'block';
|
|
1299
|
-
}
|
|
1459
|
+
const semantic_trigger = source === 'no_match' || source === 'out_of_scope';
|
|
1460
|
+
const clarif_count = this.messages.filter(msg => msg.role === 'assistant' && msg.source === 'clarification').length;
|
|
1461
|
+
const repeated_clarif = source === 'clarification' && clarif_count >= 3;
|
|
1462
|
+
if (semantic_trigger || repeated_clarif || (suggest_agent && user_msg_count >= 5)) agent_bar.style.display = 'block';
|
|
1300
1463
|
}
|
|
1301
|
-
|
|
1302
1464
|
if (suggest_agent && !this.agent_requested) {
|
|
1303
1465
|
this.addMessage('system', m.suggest_agent_escalation);
|
|
1304
1466
|
this.last_message_count += 1;
|
|
@@ -1307,7 +1469,6 @@
|
|
|
1307
1469
|
this.hideTyping();
|
|
1308
1470
|
this.addMessage('assistant', this.getMessages().error_occurred);
|
|
1309
1471
|
}
|
|
1310
|
-
|
|
1311
1472
|
this.is_loading = false;
|
|
1312
1473
|
}
|
|
1313
1474
|
|
|
@@ -1316,7 +1477,6 @@
|
|
|
1316
1477
|
this.agent_requested = true;
|
|
1317
1478
|
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
1318
1479
|
if (agent_bar) agent_bar.style.display = 'none';
|
|
1319
|
-
|
|
1320
1480
|
try {
|
|
1321
1481
|
const response = await fetch(`${this.api_url}/chat/conversations/${this.conversation_id}/request-agent`, {
|
|
1322
1482
|
method: 'POST',
|
|
@@ -1324,21 +1484,18 @@
|
|
|
1324
1484
|
body: JSON.stringify({ user_id: this.user_id }),
|
|
1325
1485
|
});
|
|
1326
1486
|
const data = await response.json();
|
|
1327
|
-
const m = this.getMessages()
|
|
1328
|
-
|
|
1329
|
-
|
|
1487
|
+
const m = this.getMessages(),
|
|
1488
|
+
status = data.result?.status;
|
|
1330
1489
|
let msg = '';
|
|
1331
1490
|
if (status === 'outside_hours') msg = m.outside_hours(data.result.next_opening);
|
|
1332
1491
|
else if (status === 'no_agents') msg = m.no_agents;
|
|
1333
1492
|
else if (status === 'waiting_agent') msg = m.waiting_agent;
|
|
1334
1493
|
else if (status === 'waiting_agent_already') msg = m.waiting_agent_already;
|
|
1335
1494
|
else if (status === 'agent') msg = m.agent_already;
|
|
1336
|
-
|
|
1337
1495
|
if (msg) {
|
|
1338
1496
|
this.addMessage('system', msg);
|
|
1339
1497
|
this.last_message_count += 1;
|
|
1340
1498
|
}
|
|
1341
|
-
|
|
1342
1499
|
if (status === 'no_agents' || status === 'outside_hours' || status === 'waiting_agent_already') {
|
|
1343
1500
|
this.agent_requested = false;
|
|
1344
1501
|
if (agent_bar) agent_bar.style.display = 'block';
|
|
@@ -1373,24 +1530,21 @@
|
|
|
1373
1530
|
const data = await response.json();
|
|
1374
1531
|
const conv = data.result?.conversation;
|
|
1375
1532
|
if (!conv) return;
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
const is_typing = conv.is_typing || false;
|
|
1533
|
+
const server_messages = conv.messages || [],
|
|
1534
|
+
server_status = conv.status,
|
|
1535
|
+
is_typing = conv.is_typing || false;
|
|
1380
1536
|
const m = this.getMessages();
|
|
1381
|
-
|
|
1382
1537
|
if (server_status === 'closed' && !this.is_closed) {
|
|
1383
1538
|
this.showClosed();
|
|
1384
1539
|
return;
|
|
1385
1540
|
}
|
|
1386
|
-
|
|
1387
1541
|
if (is_typing && server_status === 'agent') {
|
|
1388
1542
|
if (!this.querySelector('#tp-agent-typing')) {
|
|
1389
1543
|
const container = this.querySelector('#tp-messages');
|
|
1390
1544
|
const typing = document.createElement('div');
|
|
1391
|
-
typing.className = 'tp-
|
|
1545
|
+
typing.className = 'tp-msg agent';
|
|
1392
1546
|
typing.id = 'tp-agent-typing';
|
|
1393
|
-
typing.innerHTML = `<div class="tp-
|
|
1547
|
+
typing.innerHTML = `<div class="tp-msg-role">${icon('agent', 11)} Agent</div><div class="tp-bubble-msg"><div class="tp-typing"><span></span><span></span><span></span></div></div>`;
|
|
1394
1548
|
container.appendChild(typing);
|
|
1395
1549
|
container.scrollTop = container.scrollHeight;
|
|
1396
1550
|
}
|
|
@@ -1398,13 +1552,12 @@
|
|
|
1398
1552
|
const typing_el = this.querySelector('#tp-agent-typing');
|
|
1399
1553
|
if (typing_el) typing_el.remove();
|
|
1400
1554
|
}
|
|
1401
|
-
|
|
1402
1555
|
if (server_messages.length > this.last_message_count) {
|
|
1403
1556
|
const new_messages = server_messages.slice(this.last_message_count);
|
|
1404
1557
|
new_messages.forEach(msg => {
|
|
1405
1558
|
if (msg.role === 'agent') {
|
|
1406
|
-
const
|
|
1407
|
-
if (
|
|
1559
|
+
const t = this.querySelector('#tp-agent-typing');
|
|
1560
|
+
if (t) t.remove();
|
|
1408
1561
|
this.addMessage('agent', msg.content, msg.message_id || null);
|
|
1409
1562
|
} else if (msg.role === 'assistant') {
|
|
1410
1563
|
this.addMessage('assistant', msg.content, msg.message_id || null);
|
|
@@ -1414,22 +1567,23 @@
|
|
|
1414
1567
|
});
|
|
1415
1568
|
this.last_message_count = server_messages.length;
|
|
1416
1569
|
}
|
|
1417
|
-
|
|
1418
1570
|
if (server_status === 'agent' && !this.agent_mode) {
|
|
1419
1571
|
this.agent_mode = true;
|
|
1420
|
-
|
|
1421
|
-
if (subtitle) subtitle.innerHTML = `${icon('user', 12, 'margin-right:4px')} ${m.human_agent}`;
|
|
1572
|
+
this._updateSubtitle('agent');
|
|
1422
1573
|
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
1423
1574
|
if (agent_bar) agent_bar.style.display = 'none';
|
|
1575
|
+
const close_bar = this.querySelector('#tp-close-bar');
|
|
1576
|
+
if (close_bar) close_bar.style.display = 'none';
|
|
1424
1577
|
this.addMessage('system', m.agent_taken);
|
|
1425
1578
|
this.last_message_count = server_messages.length;
|
|
1426
1579
|
} else if (server_status === 'bot' && this.agent_mode) {
|
|
1427
1580
|
this.agent_mode = false;
|
|
1428
1581
|
this.agent_requested = false;
|
|
1429
|
-
|
|
1430
|
-
if (subtitle) subtitle.innerHTML = `${icon('bot', 12, 'margin-right:4px')} ${m.virtual}`;
|
|
1582
|
+
this._updateSubtitle('bot');
|
|
1431
1583
|
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
1432
1584
|
if (agent_bar) agent_bar.style.display = 'block';
|
|
1585
|
+
const close_bar = this.querySelector('#tp-close-bar');
|
|
1586
|
+
if (close_bar) close_bar.style.display = 'block';
|
|
1433
1587
|
this.addMessage('system', m.agent_released);
|
|
1434
1588
|
this.last_message_count = server_messages.length;
|
|
1435
1589
|
}
|