@developpement/tp-chatbot-widget 0.0.11 → 0.0.13
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 +3 -5
- package/src/chatbot.js +548 -397
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: none; flex-direction: column; overflow: hidden; opacity: 0; transform: scale(0.92) translateY(12px); pointer-events: none; z-index: 9999; transition: opacity 0.22s ease, transform 0.22s ease, visibility 0.22s ease; }
|
|
41
|
+
.tp-chatbot-window.open { opacity: 1; visibility: visible; 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
|
}
|
|
@@ -209,7 +317,6 @@
|
|
|
209
317
|
if (!response.ok) return;
|
|
210
318
|
const data = await response.json();
|
|
211
319
|
const t = data.result?.result;
|
|
212
|
-
console.log('[fetchTheme] open_by_default:', t.open_by_default, typeof t.open_by_default);
|
|
213
320
|
if (!t) return;
|
|
214
321
|
if (CLIENT_THEMES[this.client_id]) {
|
|
215
322
|
if (t.primary) CLIENT_THEMES[this.client_id].primary = t.primary;
|
|
@@ -225,7 +332,7 @@
|
|
|
225
332
|
};
|
|
226
333
|
}
|
|
227
334
|
} catch (e) {
|
|
228
|
-
console.warn('[tp-chatbot] fetchTheme failed
|
|
335
|
+
console.warn('[tp-chatbot] fetchTheme failed:', e.message);
|
|
229
336
|
}
|
|
230
337
|
}
|
|
231
338
|
|
|
@@ -242,6 +349,9 @@
|
|
|
242
349
|
this.view = 'list';
|
|
243
350
|
this.is_fullscreen = false;
|
|
244
351
|
this.is_minimized = false;
|
|
352
|
+
this.active_tab = 'new';
|
|
353
|
+
|
|
354
|
+
injectCSS();
|
|
245
355
|
|
|
246
356
|
this.fetchTheme().then(() => {
|
|
247
357
|
this.render();
|
|
@@ -250,11 +360,7 @@
|
|
|
250
360
|
this._initialized = true;
|
|
251
361
|
|
|
252
362
|
const theme = getClientTheme(this.client_id);
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
if (theme.open_by_default) {
|
|
256
|
-
setTimeout(() => this.toggleChat(), 300);
|
|
257
|
-
}
|
|
363
|
+
if (theme.open_by_default) setTimeout(() => this.toggleChat(), 300);
|
|
258
364
|
|
|
259
365
|
if (this.access_token) {
|
|
260
366
|
this.user_info = extractUserInfo(this.access_token);
|
|
@@ -280,8 +386,6 @@
|
|
|
280
386
|
if (this.polling_interval) clearInterval(this.polling_interval);
|
|
281
387
|
}
|
|
282
388
|
|
|
283
|
-
// ─── i18n ──────────────────────────────────────────────────────────────────
|
|
284
|
-
|
|
285
389
|
getMessages() {
|
|
286
390
|
const lang = (this.user_info?.language || 'EN').toUpperCase();
|
|
287
391
|
const msgs = {
|
|
@@ -307,6 +411,8 @@
|
|
|
307
411
|
close_btn: 'Terminer la conversation',
|
|
308
412
|
speak_agent: 'Parler à un agent',
|
|
309
413
|
write_msg: 'Écrivez votre message...',
|
|
414
|
+
tab_new: 'Nouveau',
|
|
415
|
+
tab_history: 'Historique',
|
|
310
416
|
limit_reached: 'Vous avez atteint la limite de 30 messages. Veuillez contacter un agent ou écrire à support@travelplanet.com.',
|
|
311
417
|
too_many: 'Trop de messages. Veuillez patienter avant de réessayer.',
|
|
312
418
|
error_occurred: 'Une erreur est survenue. Veuillez réessayer.',
|
|
@@ -321,6 +427,17 @@
|
|
|
321
427
|
status_waiting: 'En attente',
|
|
322
428
|
status_bot: 'Bot',
|
|
323
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',
|
|
324
441
|
},
|
|
325
442
|
EN: {
|
|
326
443
|
closed: 'Conversation closed',
|
|
@@ -344,6 +461,8 @@
|
|
|
344
461
|
close_btn: 'End conversation',
|
|
345
462
|
speak_agent: 'Talk to an agent',
|
|
346
463
|
write_msg: 'Write your message...',
|
|
464
|
+
tab_new: 'New',
|
|
465
|
+
tab_history: 'History',
|
|
347
466
|
limit_reached: 'You have reached the 30 message limit. Please contact an agent or write to support@travelplanet.com.',
|
|
348
467
|
too_many: 'Too many messages. Please wait before retrying.',
|
|
349
468
|
error_occurred: 'An error occurred. Please try again.',
|
|
@@ -358,6 +477,17 @@
|
|
|
358
477
|
status_waiting: 'Waiting',
|
|
359
478
|
status_bot: 'Bot',
|
|
360
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',
|
|
361
491
|
},
|
|
362
492
|
DE: {
|
|
363
493
|
closed: 'Gespräch beendet',
|
|
@@ -366,8 +496,8 @@
|
|
|
366
496
|
csat_negative: 'Danke, wir werden uns verbessern.',
|
|
367
497
|
new_conv: 'Neues Gespräch',
|
|
368
498
|
back_to_list: 'Zurück zur Liste',
|
|
369
|
-
agent_taken: 'Ein Agent hat Ihr Gespräch übernommen.
|
|
370
|
-
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.',
|
|
371
501
|
close_confirm: 'Möchten Sie das Gespräch beenden?',
|
|
372
502
|
terminated: 'Gespräch beendet',
|
|
373
503
|
virtual: 'Virtueller Assistent',
|
|
@@ -381,13 +511,15 @@
|
|
|
381
511
|
close_btn: 'Gespräch beenden',
|
|
382
512
|
speak_agent: 'Mit einem Agenten sprechen',
|
|
383
513
|
write_msg: 'Schreiben Sie Ihre Nachricht...',
|
|
514
|
+
tab_new: 'Neu',
|
|
515
|
+
tab_history: 'Verlauf',
|
|
384
516
|
limit_reached: 'Sie haben das Limit von 30 Nachrichten erreicht.',
|
|
385
517
|
too_many: 'Zu viele Nachrichten. Bitte warten Sie.',
|
|
386
|
-
error_occurred: 'Ein Fehler ist aufgetreten.
|
|
518
|
+
error_occurred: 'Ein Fehler ist aufgetreten.',
|
|
387
519
|
outside_hours: next => `Unser Support ist derzeit geschlossen. Nächster Termin: ${next}.`,
|
|
388
|
-
no_agents: 'Derzeit kein Agent verfügbar.
|
|
389
|
-
waiting_agent: 'Ihre Anfrage wurde übermittelt.
|
|
390
|
-
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.',
|
|
391
523
|
agent_already: 'Ein Agent betreut bereits Ihr Gespräch.',
|
|
392
524
|
suggest_agent_escalation: 'Ich kann keine passende Antwort finden. Möchten Sie mit einem Agenten sprechen?',
|
|
393
525
|
status_closed: 'Geschlossen',
|
|
@@ -395,6 +527,17 @@
|
|
|
395
527
|
status_waiting: 'Wartend',
|
|
396
528
|
status_bot: 'Bot',
|
|
397
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',
|
|
398
541
|
},
|
|
399
542
|
ES: {
|
|
400
543
|
closed: 'Conversación cerrada',
|
|
@@ -403,14 +546,14 @@
|
|
|
403
546
|
csat_negative: 'Gracias, mejoraremos.',
|
|
404
547
|
new_conv: 'Nueva conversación',
|
|
405
548
|
back_to_list: 'Volver a las conversaciones',
|
|
406
|
-
agent_taken: 'Un agente ha tomado su conversación.
|
|
407
|
-
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.',
|
|
408
551
|
close_confirm: '¿Desea finalizar esta conversación?',
|
|
409
552
|
terminated: 'Conversación terminada',
|
|
410
553
|
virtual: 'Asistente virtual',
|
|
411
554
|
human_agent: 'Agente humano',
|
|
412
555
|
loading: 'Cargando...',
|
|
413
|
-
no_conv: 'No hay conversaciones
|
|
556
|
+
no_conv: 'No hay conversaciones.',
|
|
414
557
|
new_conv_btn: 'Nueva conversación',
|
|
415
558
|
error_load: 'Error de carga.',
|
|
416
559
|
recent_conv: 'Sus conversaciones recientes',
|
|
@@ -418,20 +561,33 @@
|
|
|
418
561
|
close_btn: 'Finalizar conversación',
|
|
419
562
|
speak_agent: 'Hablar con un agente',
|
|
420
563
|
write_msg: 'Escriba su mensaje...',
|
|
564
|
+
tab_new: 'Nuevo',
|
|
565
|
+
tab_history: 'Historial',
|
|
421
566
|
limit_reached: 'Ha alcanzado el límite de 30 mensajes.',
|
|
422
|
-
too_many: 'Demasiados mensajes.
|
|
423
|
-
error_occurred: 'Se produjo un error.
|
|
567
|
+
too_many: 'Demasiados mensajes.',
|
|
568
|
+
error_occurred: 'Se produjo un error.',
|
|
424
569
|
outside_hours: next => `Nuestro soporte está cerrado. Próximo horario: ${next}.`,
|
|
425
|
-
no_agents: 'No hay agentes disponibles.
|
|
426
|
-
waiting_agent: 'Su solicitud ha sido enviada.
|
|
427
|
-
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.',
|
|
428
573
|
agent_already: 'Un agente ya está gestionando su conversación.',
|
|
429
|
-
suggest_agent_escalation: '
|
|
574
|
+
suggest_agent_escalation: '¿Desea hablar con un agente?',
|
|
430
575
|
status_closed: 'Cerrado',
|
|
431
576
|
status_agent: 'Agente',
|
|
432
577
|
status_waiting: 'Esperando',
|
|
433
578
|
status_bot: 'Bot',
|
|
434
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',
|
|
435
591
|
},
|
|
436
592
|
IT: {
|
|
437
593
|
closed: 'Conversazione chiusa',
|
|
@@ -441,13 +597,13 @@
|
|
|
441
597
|
new_conv: 'Nuova conversazione',
|
|
442
598
|
back_to_list: 'Torna alle conversazioni',
|
|
443
599
|
agent_taken: 'Un agente ha preso in carico la sua conversazione.',
|
|
444
|
-
agent_released: 'Il nostro assistente virtuale è tornato.
|
|
600
|
+
agent_released: 'Il nostro assistente virtuale è tornato.',
|
|
445
601
|
close_confirm: 'Vuole terminare questa conversazione?',
|
|
446
602
|
terminated: 'Conversazione terminata',
|
|
447
603
|
virtual: 'Assistente virtuale',
|
|
448
604
|
human_agent: 'Agente umano',
|
|
449
605
|
loading: 'Caricamento...',
|
|
450
|
-
no_conv: 'Nessuna conversazione
|
|
606
|
+
no_conv: 'Nessuna conversazione.',
|
|
451
607
|
new_conv_btn: 'Nuova conversazione',
|
|
452
608
|
error_load: 'Errore di caricamento.',
|
|
453
609
|
recent_conv: 'Le sue conversazioni recenti',
|
|
@@ -455,20 +611,33 @@
|
|
|
455
611
|
close_btn: 'Termina conversazione',
|
|
456
612
|
speak_agent: 'Parla con un agente',
|
|
457
613
|
write_msg: 'Scrivi il tuo messaggio...',
|
|
614
|
+
tab_new: 'Nuovo',
|
|
615
|
+
tab_history: 'Cronologia',
|
|
458
616
|
limit_reached: 'Hai raggiunto il limite di 30 messaggi.',
|
|
459
|
-
too_many: 'Troppi messaggi.
|
|
460
|
-
error_occurred: 'Si è verificato un errore.
|
|
617
|
+
too_many: 'Troppi messaggi.',
|
|
618
|
+
error_occurred: 'Si è verificato un errore.',
|
|
461
619
|
outside_hours: next => `Il supporto è chiuso. Prossima disponibilità: ${next}.`,
|
|
462
|
-
no_agents: 'Nessun agente disponibile.
|
|
463
|
-
waiting_agent: 'Richiesta inviata.
|
|
464
|
-
waiting_agent_already: 'È già in coda.
|
|
620
|
+
no_agents: 'Nessun agente disponibile.',
|
|
621
|
+
waiting_agent: 'Richiesta inviata.',
|
|
622
|
+
waiting_agent_already: 'È già in coda.',
|
|
465
623
|
agent_already: 'Un agente sta già gestendo la conversazione.',
|
|
466
|
-
suggest_agent_escalation: '
|
|
624
|
+
suggest_agent_escalation: 'Desidera parlare con un agente?',
|
|
467
625
|
status_closed: 'Chiuso',
|
|
468
626
|
status_agent: 'Agente',
|
|
469
627
|
status_waiting: 'Attesa',
|
|
470
628
|
status_bot: 'Bot',
|
|
471
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',
|
|
472
641
|
},
|
|
473
642
|
NL: {
|
|
474
643
|
closed: 'Gesprek gesloten',
|
|
@@ -478,13 +647,13 @@
|
|
|
478
647
|
new_conv: 'Nieuw gesprek',
|
|
479
648
|
back_to_list: 'Terug naar gesprekken',
|
|
480
649
|
agent_taken: 'Een agent heeft uw gesprek overgenomen.',
|
|
481
|
-
agent_released: 'Onze virtuele assistent is terug.
|
|
650
|
+
agent_released: 'Onze virtuele assistent is terug.',
|
|
482
651
|
close_confirm: 'Wilt u dit gesprek beëindigen?',
|
|
483
652
|
terminated: 'Gesprek beëindigd',
|
|
484
653
|
virtual: 'Virtuele assistent',
|
|
485
654
|
human_agent: 'Menselijke agent',
|
|
486
655
|
loading: 'Laden...',
|
|
487
|
-
no_conv: 'Geen gesprekken
|
|
656
|
+
no_conv: 'Geen gesprekken.',
|
|
488
657
|
new_conv_btn: 'Nieuw gesprek',
|
|
489
658
|
error_load: 'Laadout.',
|
|
490
659
|
recent_conv: 'Uw recente gesprekken',
|
|
@@ -492,20 +661,33 @@
|
|
|
492
661
|
close_btn: 'Gesprek beëindigen',
|
|
493
662
|
speak_agent: 'Praat met een agent',
|
|
494
663
|
write_msg: 'Schrijf uw bericht...',
|
|
664
|
+
tab_new: 'Nieuw',
|
|
665
|
+
tab_history: 'Geschiedenis',
|
|
495
666
|
limit_reached: 'U heeft de limiet van 30 berichten bereikt.',
|
|
496
|
-
too_many: 'Te veel berichten.
|
|
497
|
-
error_occurred: 'Er is een fout opgetreden.
|
|
498
|
-
outside_hours: next => `
|
|
499
|
-
no_agents: 'Geen agent beschikbaar.
|
|
500
|
-
waiting_agent: 'Uw verzoek is verzonden.
|
|
501
|
-
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.',
|
|
502
673
|
agent_already: 'Een agent beheert uw gesprek al.',
|
|
503
|
-
suggest_agent_escalation: '
|
|
674
|
+
suggest_agent_escalation: 'Wilt u met een agent spreken?',
|
|
504
675
|
status_closed: 'Gesloten',
|
|
505
676
|
status_agent: 'Agent',
|
|
506
677
|
status_waiting: 'Wachten',
|
|
507
678
|
status_bot: 'Bot',
|
|
508
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',
|
|
509
691
|
},
|
|
510
692
|
};
|
|
511
693
|
return msgs[lang] || msgs['EN'];
|
|
@@ -525,8 +707,6 @@
|
|
|
525
707
|
return msgs[lang] || msgs['EN'];
|
|
526
708
|
}
|
|
527
709
|
|
|
528
|
-
// ─── Theme ─────────────────────────────────────────────────────────────────
|
|
529
|
-
|
|
530
710
|
getThemeColor() {
|
|
531
711
|
return getClientTheme(this.client_id).primary;
|
|
532
712
|
}
|
|
@@ -547,30 +727,28 @@
|
|
|
547
727
|
const style = document.createElement('style');
|
|
548
728
|
style.id = 'tp-chatbot-theme';
|
|
549
729
|
style.textContent = `
|
|
550
|
-
.tp-chatbot-bubble { background: linear-gradient(135deg, ${color}, ${dark}) !important; box-shadow: 0 4px
|
|
551
|
-
.tp-
|
|
552
|
-
.tp-
|
|
553
|
-
.tp-
|
|
554
|
-
.tp-
|
|
555
|
-
.tp-
|
|
556
|
-
.tp-
|
|
557
|
-
.tp-
|
|
558
|
-
.tp-
|
|
559
|
-
.tp-chatbot-agent-btn:hover { background: ${color} !important; color: white !important; }
|
|
560
|
-
.tp-chatbot-typing span { background: ${color} !important; }
|
|
561
|
-
#tp-new-conversation { background: linear-gradient(135deg, ${color}, ${dark}) !important; }
|
|
562
|
-
.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; }
|
|
563
739
|
.tp-conv-new-btn { background: linear-gradient(135deg, ${color}, ${dark}) !important; }
|
|
564
|
-
.tp-
|
|
565
|
-
.tp-
|
|
566
|
-
.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; }
|
|
567
747
|
`;
|
|
568
748
|
document.head.appendChild(style);
|
|
569
|
-
|
|
570
749
|
const position = getClientTheme(this.client_id).position || 'right';
|
|
571
750
|
const host = this.querySelector('.tp-chatbot-host');
|
|
572
751
|
if (host) {
|
|
573
|
-
host.style.width = WINDOW_WIDTH;
|
|
574
752
|
host.style.left = position === 'left' ? '24px' : 'auto';
|
|
575
753
|
host.style.right = position === 'left' ? 'auto' : '24px';
|
|
576
754
|
}
|
|
@@ -579,7 +757,7 @@
|
|
|
579
757
|
bubble.style.marginLeft = position === 'left' ? '0' : 'auto';
|
|
580
758
|
bubble.style.marginRight = position === 'left' ? 'auto' : '0';
|
|
581
759
|
}
|
|
582
|
-
const win = this.querySelector('
|
|
760
|
+
const win = this.querySelector('#tp-window');
|
|
583
761
|
if (win) {
|
|
584
762
|
win.style.left = '0';
|
|
585
763
|
win.style.right = 'auto';
|
|
@@ -587,47 +765,55 @@
|
|
|
587
765
|
}
|
|
588
766
|
}
|
|
589
767
|
|
|
590
|
-
// ─── Render ────────────────────────────────────────────────────────────────
|
|
591
|
-
|
|
592
768
|
render() {
|
|
593
769
|
const theme = getClientTheme(this.client_id);
|
|
770
|
+
const m = this.getMessages();
|
|
594
771
|
this.innerHTML = `
|
|
595
772
|
<div class="tp-chatbot-host">
|
|
596
773
|
<div class="tp-chatbot-window" id="tp-window">
|
|
597
|
-
<div class="tp-
|
|
598
|
-
<div class="tp-
|
|
599
|
-
|
|
600
|
-
<div class="tp-
|
|
601
|
-
|
|
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>
|
|
602
792
|
</div>
|
|
603
|
-
<button id="tp-sound-btn" class="tp-header-btn" title="Sound">${icon('bell', 16)}</button>
|
|
604
|
-
<button id="tp-minimize-btn" class="tp-header-btn" title="Minimize">${icon('minimize', 16)}</button>
|
|
605
|
-
<button id="tp-maximize-btn" class="tp-header-btn" title="Fullscreen">${icon('restore', 16)}</button>
|
|
606
|
-
<button id="tp-back-btn" class="tp-header-btn" style="display:none;" title="Back">${icon('back', 16)}</button>
|
|
607
793
|
</div>
|
|
608
|
-
<div class="tp-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
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>
|
|
612
800
|
</button>
|
|
613
801
|
</div>
|
|
614
|
-
<div class="tp-
|
|
615
|
-
<textarea class="tp-
|
|
616
|
-
<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>
|
|
617
805
|
</div>
|
|
618
|
-
<div id="tp-close-bar" style="
|
|
619
|
-
<button
|
|
620
|
-
<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>
|
|
621
809
|
</button>
|
|
622
810
|
</div>
|
|
623
811
|
</div>
|
|
624
|
-
<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>
|
|
625
813
|
</div>
|
|
626
814
|
`;
|
|
627
815
|
}
|
|
628
816
|
|
|
629
|
-
// ─── Window controls ───────────────────────────────────────────────────────
|
|
630
|
-
|
|
631
817
|
minimize() {
|
|
632
818
|
const win = this.querySelector('#tp-window');
|
|
633
819
|
if (this.is_fullscreen) this.exitFullscreen(false);
|
|
@@ -637,8 +823,8 @@
|
|
|
637
823
|
}
|
|
638
824
|
|
|
639
825
|
enterFullscreen() {
|
|
640
|
-
const win = this.querySelector('#tp-window')
|
|
641
|
-
|
|
826
|
+
const win = this.querySelector('#tp-window'),
|
|
827
|
+
host = this.querySelector('.tp-chatbot-host');
|
|
642
828
|
this.is_fullscreen = true;
|
|
643
829
|
this.is_minimized = false;
|
|
644
830
|
win.style.position = 'fixed';
|
|
@@ -650,19 +836,19 @@
|
|
|
650
836
|
win.style.zIndex = '99999';
|
|
651
837
|
host.style.width = '100vw';
|
|
652
838
|
const btn = this.querySelector('#tp-maximize-btn');
|
|
653
|
-
if (btn) btn.innerHTML = icon('restore',
|
|
839
|
+
if (btn) btn.innerHTML = icon('restore', 14);
|
|
654
840
|
}
|
|
655
841
|
|
|
656
842
|
exitFullscreen(restore_height = true) {
|
|
657
|
-
const win = this.querySelector('#tp-window')
|
|
658
|
-
|
|
659
|
-
|
|
843
|
+
const win = this.querySelector('#tp-window'),
|
|
844
|
+
position = getClientTheme(this.client_id).position || 'right',
|
|
845
|
+
host = this.querySelector('.tp-chatbot-host');
|
|
660
846
|
this.is_fullscreen = false;
|
|
661
847
|
win.style.position = 'absolute';
|
|
662
848
|
win.style.top = '';
|
|
663
849
|
win.style.left = '0';
|
|
664
850
|
win.style.width = WINDOW_WIDTH;
|
|
665
|
-
win.style.borderRadius = '
|
|
851
|
+
win.style.borderRadius = '20px';
|
|
666
852
|
win.style.zIndex = '';
|
|
667
853
|
host.style.width = WINDOW_WIDTH;
|
|
668
854
|
host.style.left = position === 'left' ? '24px' : 'auto';
|
|
@@ -670,33 +856,33 @@
|
|
|
670
856
|
if (restore_height) win.style.height = WINDOW_HEIGHT;
|
|
671
857
|
win.style.overflow = 'hidden';
|
|
672
858
|
const btn = this.querySelector('#tp-maximize-btn');
|
|
673
|
-
if (btn) btn.innerHTML = icon('restore',
|
|
859
|
+
if (btn) btn.innerHTML = icon('restore', 14);
|
|
674
860
|
}
|
|
675
861
|
|
|
676
|
-
// ─── Events ────────────────────────────────────────────────────────────────
|
|
677
|
-
|
|
678
862
|
attachEvents() {
|
|
679
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
|
+
});
|
|
680
867
|
this.querySelector('#tp-send').addEventListener('click', () => this.sendMessage());
|
|
681
868
|
this.querySelector('#tp-agent-btn').addEventListener('click', () => this.requestAgent());
|
|
682
|
-
this.querySelector('#tp-back-btn').addEventListener('click', () =>
|
|
683
|
-
|
|
869
|
+
this.querySelector('#tp-back-btn').addEventListener('click', () => {
|
|
870
|
+
this.setTab('new');
|
|
871
|
+
this.showConversationList();
|
|
872
|
+
});
|
|
684
873
|
this.querySelector('#tp-input').addEventListener('keydown', e => {
|
|
685
874
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
686
875
|
e.preventDefault();
|
|
687
876
|
this.sendMessage();
|
|
688
877
|
}
|
|
689
878
|
});
|
|
690
|
-
|
|
691
879
|
this.querySelector('#tp-sound-btn').addEventListener('click', () => {
|
|
692
880
|
this.sound_enabled = !this.sound_enabled;
|
|
693
|
-
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);
|
|
694
882
|
});
|
|
695
|
-
|
|
696
883
|
this.querySelector('#tp-close-btn').addEventListener('click', () => {
|
|
697
884
|
if (window.confirm(this.getMessages().close_confirm)) this.closeByUser();
|
|
698
885
|
});
|
|
699
|
-
|
|
700
886
|
this.querySelector('#tp-minimize-btn').addEventListener('click', () => {
|
|
701
887
|
if (this.is_minimized) {
|
|
702
888
|
this.is_minimized = false;
|
|
@@ -707,7 +893,6 @@
|
|
|
707
893
|
this.minimize();
|
|
708
894
|
}
|
|
709
895
|
});
|
|
710
|
-
|
|
711
896
|
this.querySelector('#tp-maximize-btn').addEventListener('click', () => {
|
|
712
897
|
if (this.is_fullscreen) {
|
|
713
898
|
this.exitFullscreen(true);
|
|
@@ -721,40 +906,58 @@
|
|
|
721
906
|
this.enterFullscreen();
|
|
722
907
|
}
|
|
723
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';
|
|
724
927
|
}
|
|
725
928
|
|
|
726
929
|
updateUILanguage() {
|
|
727
930
|
const m = this.getMessages();
|
|
728
931
|
const agent_btn = this.querySelector('#tp-agent-btn');
|
|
729
932
|
if (agent_btn)
|
|
730
|
-
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>`;
|
|
731
934
|
const close_btn = this.querySelector('#tp-close-btn');
|
|
732
935
|
if (close_btn)
|
|
733
|
-
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>`;
|
|
734
937
|
const input = this.querySelector('#tp-input');
|
|
735
938
|
if (input) input.placeholder = m.write_msg;
|
|
736
|
-
const subtitle = this.querySelector('#tp-subtitle');
|
|
737
|
-
if (subtitle) subtitle.innerHTML = `${icon('bot', 12, 'margin-right:4px')} ${m.virtual}`;
|
|
738
939
|
}
|
|
739
940
|
|
|
740
941
|
toggleChat() {
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
} else {
|
|
753
|
-
window_el.classList.remove('open');
|
|
754
|
-
bubble.innerHTML = icon('msg', 24, 'color:white');
|
|
755
|
-
if (this.is_fullscreen) this.exitFullscreen(true);
|
|
756
|
-
}
|
|
942
|
+
this.is_open = !this.is_open;
|
|
943
|
+
const window_el = this.querySelector('#tp-window');
|
|
944
|
+
const bubble = this.querySelector('#tp-bubble');
|
|
945
|
+
if (this.is_open) {
|
|
946
|
+
window_el.style.display = 'flex';
|
|
947
|
+
setTimeout(() => window_el.classList.add('open'), 10);
|
|
948
|
+
bubble.innerHTML = icon('close', 22, 'color:white');
|
|
949
|
+
if (this.is_minimized) {
|
|
950
|
+
this.is_minimized = false;
|
|
951
|
+
window_el.style.height = WINDOW_HEIGHT;
|
|
952
|
+
window_el.style.overflow = 'hidden';
|
|
757
953
|
}
|
|
954
|
+
} else {
|
|
955
|
+
window_el.classList.remove('open');
|
|
956
|
+
setTimeout(() => { window_el.style.display = 'none'; }, 250);
|
|
957
|
+
bubble.innerHTML = icon('msg', 22, 'color:white');
|
|
958
|
+
if (this.is_fullscreen) this.exitFullscreen(true);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
758
961
|
|
|
759
962
|
getHeaders(content_type = false) {
|
|
760
963
|
const headers = {};
|
|
@@ -773,31 +976,20 @@
|
|
|
773
976
|
return data.result?.conversation_id;
|
|
774
977
|
}
|
|
775
978
|
|
|
776
|
-
// ─── Suggestions ──────────────────────────────────────────────────────────
|
|
777
|
-
|
|
778
979
|
showSuggestions(suggestions) {
|
|
779
980
|
const existing = this.querySelector('#tp-suggestions');
|
|
780
981
|
if (existing) existing.remove();
|
|
781
982
|
if (!suggestions || suggestions.length === 0) return;
|
|
782
983
|
const lang = (this.user_info?.language || 'EN').toUpperCase();
|
|
783
|
-
const color = this.getThemeColor();
|
|
784
984
|
const container = this.querySelector('#tp-messages');
|
|
785
985
|
const wrap = document.createElement('div');
|
|
786
986
|
wrap.id = 'tp-suggestions';
|
|
787
|
-
wrap.
|
|
987
|
+
wrap.className = 'tp-suggestions';
|
|
788
988
|
suggestions.forEach(s => {
|
|
789
989
|
const label = s[lang] || s['EN'];
|
|
790
990
|
const btn = document.createElement('button');
|
|
791
|
-
btn.
|
|
991
|
+
btn.className = 'tp-suggestion-btn';
|
|
792
992
|
btn.textContent = `→ ${label}`;
|
|
793
|
-
btn.addEventListener('mouseenter', () => {
|
|
794
|
-
btn.style.background = color;
|
|
795
|
-
btn.style.color = 'white';
|
|
796
|
-
});
|
|
797
|
-
btn.addEventListener('mouseleave', () => {
|
|
798
|
-
btn.style.background = 'white';
|
|
799
|
-
btn.style.color = color;
|
|
800
|
-
});
|
|
801
993
|
btn.addEventListener('click', () => {
|
|
802
994
|
const sugg_el = this.querySelector('#tp-suggestions');
|
|
803
995
|
if (sugg_el) sugg_el.remove();
|
|
@@ -813,8 +1005,6 @@
|
|
|
813
1005
|
container.scrollTop = container.scrollHeight;
|
|
814
1006
|
}
|
|
815
1007
|
|
|
816
|
-
// ─── Conversation List ─────────────────────────────────────────────────────
|
|
817
|
-
|
|
818
1008
|
async showConversationList() {
|
|
819
1009
|
this.view = 'list';
|
|
820
1010
|
const m = this.getMessages();
|
|
@@ -822,94 +1012,107 @@
|
|
|
822
1012
|
clearInterval(this.polling_interval);
|
|
823
1013
|
this.polling_interval = null;
|
|
824
1014
|
}
|
|
825
|
-
|
|
826
1015
|
const subtitle = this.querySelector('#tp-subtitle');
|
|
827
|
-
if (subtitle) subtitle.innerHTML = `${icon('bot',
|
|
1016
|
+
if (subtitle) subtitle.innerHTML = `${icon('bot', 11)} ${m.virtual}`;
|
|
828
1017
|
this.querySelector('#tp-back-btn').style.display = 'none';
|
|
829
|
-
this.querySelector('#tp-
|
|
1018
|
+
this.querySelector('#tp-input-bar').style.display = 'none';
|
|
830
1019
|
this.querySelector('#tp-agent-bar').style.display = 'none';
|
|
831
1020
|
this.querySelector('#tp-close-bar').style.display = 'none';
|
|
832
|
-
|
|
1021
|
+
this.showTabs(true);
|
|
833
1022
|
const container = this.querySelector('#tp-messages');
|
|
834
|
-
container.
|
|
835
|
-
container.innerHTML = `<div style="padding:16px;text-align:center;color:#aaa;font-size:12px;">${m.loading}</div>`;
|
|
836
|
-
|
|
1023
|
+
container.innerHTML = `<div style="padding:24px 16px;text-align:center;color:#ccc;font-size:12px;">${m.loading}</div>`;
|
|
837
1024
|
try {
|
|
838
1025
|
const url = `${this.api_url}/chat/conversations?user_id=${encodeURIComponent(this.user_id)}&limit=5`;
|
|
839
1026
|
const response = await fetch(url, { headers: this.getHeaders() });
|
|
840
1027
|
const data = await response.json();
|
|
841
1028
|
const conversations = data.result?.conversations || [];
|
|
842
1029
|
const color = this.getThemeColor();
|
|
843
|
-
|
|
844
1030
|
container.innerHTML = '';
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
empty.innerHTML = `<div style="margin-bottom:8px;opacity:0.4;">${icon('msg', 32)}</div>${m.no_conv}`;
|
|
861
|
-
container.appendChild(empty);
|
|
862
|
-
} else {
|
|
863
|
-
conversations.forEach(conv => {
|
|
864
|
-
const item = document.createElement('div');
|
|
865
|
-
item.className = 'tp-conv-item';
|
|
866
|
-
item.style.cssText =
|
|
867
|
-
'padding:14px 16px;border-bottom:1px solid #f5f5f5;cursor:pointer;transition:background 0.15s;display:flex;justify-content:space-between;align-items:center;';
|
|
868
|
-
|
|
869
|
-
const is_closed = conv.status === 'closed';
|
|
1031
|
+
if (this.active_tab === 'new') {
|
|
1032
|
+
const greeting = document.createElement('div');
|
|
1033
|
+
greeting.style.cssText = 'padding:20px 16px 8px;';
|
|
1034
|
+
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>`;
|
|
1035
|
+
container.appendChild(greeting);
|
|
1036
|
+
if (conversations.length === 0) {
|
|
1037
|
+
const empty = document.createElement('div');
|
|
1038
|
+
empty.style.cssText =
|
|
1039
|
+
'padding:20px 16px;text-align:center;color:#ccc;font-size:13px;display:flex;flex-direction:column;align-items:center;gap:10px;';
|
|
1040
|
+
empty.innerHTML = `<div style="opacity:0.35;">${icon('msg', 36)}</div>${m.no_conv}`;
|
|
1041
|
+
container.appendChild(empty);
|
|
1042
|
+
} else {
|
|
1043
|
+
const last_conv = conversations[0];
|
|
1044
|
+
const is_closed = last_conv.status === 'closed';
|
|
1045
|
+
const badge_color = last_conv.status === 'waiting_agent' ? '#f59e0b' : color;
|
|
870
1046
|
const status_label = is_closed
|
|
871
1047
|
? m.status_closed
|
|
872
|
-
:
|
|
1048
|
+
: last_conv.status === 'agent'
|
|
873
1049
|
? m.status_agent
|
|
874
|
-
:
|
|
1050
|
+
: last_conv.status === 'waiting_agent'
|
|
875
1051
|
? m.status_waiting
|
|
876
1052
|
: m.status_bot;
|
|
877
|
-
|
|
878
|
-
const badge_color = is_closed ? color : conv.status === 'waiting_agent' ? '#f59e0b' : color;
|
|
879
|
-
const date = new Date(conv.updated_at).toLocaleDateString('fr-FR', {
|
|
1053
|
+
const date = new Date(last_conv.updated_at).toLocaleDateString('fr-FR', {
|
|
880
1054
|
day: '2-digit',
|
|
881
1055
|
month: '2-digit',
|
|
882
1056
|
hour: '2-digit',
|
|
883
1057
|
minute: '2-digit',
|
|
884
1058
|
});
|
|
885
|
-
|
|
886
|
-
item.
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
item.addEventListener('click', () => this.openConversation(
|
|
1059
|
+
const item = document.createElement('div');
|
|
1060
|
+
item.style.cssText =
|
|
1061
|
+
'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;';
|
|
1062
|
+
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>`;
|
|
1063
|
+
item.addEventListener('mouseenter', () => {
|
|
1064
|
+
item.style.boxShadow = '0 3px 12px rgba(0,0,0,0.12)';
|
|
1065
|
+
});
|
|
1066
|
+
item.addEventListener('mouseleave', () => {
|
|
1067
|
+
item.style.boxShadow = '0 1px 6px rgba(0,0,0,0.07)';
|
|
1068
|
+
});
|
|
1069
|
+
item.addEventListener('click', () => this.openConversation(last_conv.conversation_id, is_closed));
|
|
896
1070
|
container.appendChild(item);
|
|
897
|
-
}
|
|
1071
|
+
}
|
|
1072
|
+
const btn_wrap = document.createElement('div');
|
|
1073
|
+
btn_wrap.style.cssText = 'padding:4px 16px 16px;';
|
|
1074
|
+
btn_wrap.innerHTML = `<button class="tp-new-conv-btn">${icon('msg_plus', 15, 'color:white')} ${m.new_conv_btn}</button>`;
|
|
1075
|
+
btn_wrap.querySelector('button').addEventListener('click', () => this.startNewConversation());
|
|
1076
|
+
container.appendChild(btn_wrap);
|
|
1077
|
+
} else {
|
|
1078
|
+
const greeting = document.createElement('div');
|
|
1079
|
+
greeting.style.cssText = 'padding:16px 16px 8px;';
|
|
1080
|
+
greeting.innerHTML = `<div style="font-size:12px;font-weight:600;color:#aaa;text-transform:uppercase;letter-spacing:0.5px;">${m.recent_conv}</div>`;
|
|
1081
|
+
container.appendChild(greeting);
|
|
1082
|
+
if (conversations.length === 0) {
|
|
1083
|
+
const empty = document.createElement('div');
|
|
1084
|
+
empty.style.cssText =
|
|
1085
|
+
'padding:32px 16px;text-align:center;color:#ccc;font-size:13px;display:flex;flex-direction:column;align-items:center;gap:10px;';
|
|
1086
|
+
empty.innerHTML = `<div style="opacity:0.35;">${icon('history', 36)}</div>${m.no_conv}`;
|
|
1087
|
+
container.appendChild(empty);
|
|
1088
|
+
} else {
|
|
1089
|
+
conversations.forEach(conv => {
|
|
1090
|
+
const is_closed = conv.status === 'closed';
|
|
1091
|
+
const badge_color = conv.status === 'waiting_agent' ? '#f59e0b' : color;
|
|
1092
|
+
const status_label = is_closed
|
|
1093
|
+
? m.status_closed
|
|
1094
|
+
: conv.status === 'agent'
|
|
1095
|
+
? m.status_agent
|
|
1096
|
+
: conv.status === 'waiting_agent'
|
|
1097
|
+
? m.status_waiting
|
|
1098
|
+
: m.status_bot;
|
|
1099
|
+
const date = new Date(conv.updated_at).toLocaleDateString('fr-FR', {
|
|
1100
|
+
day: '2-digit',
|
|
1101
|
+
month: '2-digit',
|
|
1102
|
+
hour: '2-digit',
|
|
1103
|
+
minute: '2-digit',
|
|
1104
|
+
});
|
|
1105
|
+
const item = document.createElement('div');
|
|
1106
|
+
item.className = 'tp-conv-item';
|
|
1107
|
+
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>`;
|
|
1108
|
+
item.addEventListener('click', () => this.openConversation(conv.conversation_id, is_closed));
|
|
1109
|
+
container.appendChild(item);
|
|
1110
|
+
});
|
|
1111
|
+
}
|
|
898
1112
|
}
|
|
899
|
-
|
|
900
|
-
// New conversation button
|
|
901
|
-
const new_btn_wrap = document.createElement('div');
|
|
902
|
-
new_btn_wrap.style.cssText = 'padding:16px;';
|
|
903
|
-
new_btn_wrap.innerHTML = `
|
|
904
|
-
<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;">
|
|
905
|
-
${icon('msg_plus', 16, 'color:white')} ${m.new_conv_btn}
|
|
906
|
-
</button>
|
|
907
|
-
`;
|
|
908
|
-
new_btn_wrap.querySelector('button').addEventListener('click', () => this.startNewConversation());
|
|
909
|
-
container.appendChild(new_btn_wrap);
|
|
910
1113
|
} catch (e) {
|
|
911
1114
|
console.error('showConversationList error:', e);
|
|
912
|
-
container.innerHTML = `<div style="padding:16px;text-align:center;color:#ef4444;font-size:13px;">${
|
|
1115
|
+
container.innerHTML = `<div style="padding:24px 16px;text-align:center;color:#ef4444;font-size:13px;">${this.getMessages().error_load}</div>`;
|
|
913
1116
|
}
|
|
914
1117
|
}
|
|
915
1118
|
|
|
@@ -921,56 +1124,42 @@
|
|
|
921
1124
|
this.is_closed = is_closed;
|
|
922
1125
|
this.agent_mode = false;
|
|
923
1126
|
this.agent_requested = false;
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
this.querySelector('#tp-back-btn').style.display = 'block';
|
|
1127
|
+
this.querySelector('#tp-back-btn').style.display = 'flex';
|
|
1128
|
+
this.showTabs(false);
|
|
927
1129
|
const container = this.querySelector('#tp-messages');
|
|
928
|
-
container.style.background = '#f5f5f7';
|
|
929
|
-
container.style.padding = '16px';
|
|
930
1130
|
container.innerHTML = '';
|
|
931
|
-
|
|
932
|
-
const input_bar = this.querySelector('#tp-chatbot-input-bar');
|
|
933
|
-
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
934
|
-
const close_bar = this.querySelector('#tp-close-bar');
|
|
935
|
-
|
|
936
1131
|
try {
|
|
937
1132
|
const url = `${this.api_url}/chat/conversations/${conversation_id}?user_id=${encodeURIComponent(this.user_id)}`;
|
|
938
1133
|
const response = await fetch(url, { headers: this.getHeaders() });
|
|
939
1134
|
const data = await response.json();
|
|
940
1135
|
const conv = data.result?.conversation;
|
|
941
1136
|
if (!conv) return;
|
|
942
|
-
|
|
943
1137
|
conv.messages.forEach(msg => {
|
|
944
|
-
if (['user', 'assistant', 'agent', 'system'].includes(msg.role))
|
|
945
|
-
this.addMessage(msg.role, msg.content, msg.message_id || null);
|
|
946
|
-
}
|
|
1138
|
+
if (['user', 'assistant', 'agent', 'system'].includes(msg.role)) this.addMessage(msg.role, msg.content, msg.message_id || null);
|
|
947
1139
|
});
|
|
948
1140
|
this.last_message_count = conv.messages.length;
|
|
949
|
-
|
|
950
1141
|
if (conv.status === 'closed') {
|
|
951
1142
|
this.showClosed(conv.csat || null);
|
|
952
1143
|
return;
|
|
953
1144
|
}
|
|
954
|
-
|
|
1145
|
+
const input_bar = this.querySelector('#tp-input-bar'),
|
|
1146
|
+
agent_bar = this.querySelector('#tp-agent-bar'),
|
|
1147
|
+
close_bar = this.querySelector('#tp-close-bar');
|
|
955
1148
|
if (conv.status === 'bot' || conv.status === 'waiting_agent') {
|
|
956
1149
|
if (close_bar) close_bar.style.display = 'block';
|
|
957
1150
|
} else {
|
|
958
1151
|
if (close_bar) close_bar.style.display = 'none';
|
|
959
1152
|
}
|
|
960
|
-
|
|
961
1153
|
if (conv.status === 'agent') {
|
|
962
1154
|
this.agent_mode = true;
|
|
963
|
-
|
|
964
|
-
if (subtitle) subtitle.innerHTML = `${icon('user', 12, 'margin-right:4px')} ${m.human_agent}`;
|
|
1155
|
+
this._updateSubtitle('agent');
|
|
965
1156
|
if (agent_bar) agent_bar.style.display = 'none';
|
|
966
1157
|
if (input_bar) input_bar.style.display = 'flex';
|
|
967
1158
|
} else {
|
|
968
1159
|
if (input_bar) input_bar.style.display = 'flex';
|
|
969
1160
|
if (agent_bar) agent_bar.style.display = 'none';
|
|
970
|
-
|
|
971
|
-
if (subtitle) subtitle.innerHTML = `${icon('bot', 12, 'margin-right:4px')} ${m.virtual}`;
|
|
1161
|
+
this._updateSubtitle('bot');
|
|
972
1162
|
}
|
|
973
|
-
|
|
974
1163
|
this.updateUILanguage();
|
|
975
1164
|
this.polling_interval = setInterval(() => this.pollMessages(), 3000);
|
|
976
1165
|
} catch (e) {
|
|
@@ -978,6 +1167,7 @@
|
|
|
978
1167
|
}
|
|
979
1168
|
}
|
|
980
1169
|
|
|
1170
|
+
// ─── FIXED: conv créée au premier sendMessage, pas à l'ouverture ──────────
|
|
981
1171
|
async startNewConversation() {
|
|
982
1172
|
this.view = 'chat';
|
|
983
1173
|
this.is_closed = false;
|
|
@@ -985,71 +1175,78 @@
|
|
|
985
1175
|
this.agent_requested = false;
|
|
986
1176
|
this.messages = [];
|
|
987
1177
|
this.last_message_count = 0;
|
|
988
|
-
this.conversation_id = null;
|
|
989
|
-
|
|
990
|
-
const m = this.getMessages();
|
|
991
|
-
this.querySelector('#tp-back-btn').style.display = 'block';
|
|
992
|
-
this.querySelector('#tp-close-bar').style.display = 'block';
|
|
1178
|
+
this.conversation_id = null; // pas encore créée
|
|
993
1179
|
|
|
1180
|
+
this.querySelector('#tp-back-btn').style.display = 'flex';
|
|
1181
|
+
this.querySelector('#tp-close-bar').style.display = 'none'; // cachée tant que pas de conv
|
|
1182
|
+
this.showTabs(false);
|
|
994
1183
|
const container = this.querySelector('#tp-messages');
|
|
995
|
-
container.style.background = '#f5f5f7';
|
|
996
|
-
container.style.padding = '16px';
|
|
997
1184
|
container.innerHTML = '';
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
if (subtitle) subtitle.innerHTML = `${icon('bot', 12, 'margin-right:4px')} ${m.virtual}`;
|
|
1001
|
-
|
|
1002
|
-
this.querySelector('#tp-chatbot-input-bar').style.display = 'flex';
|
|
1185
|
+
this._updateSubtitle('bot');
|
|
1186
|
+
this.querySelector('#tp-input-bar').style.display = 'flex';
|
|
1003
1187
|
this.querySelector('#tp-agent-bar').style.display = 'none';
|
|
1004
1188
|
this.updateUILanguage();
|
|
1005
|
-
|
|
1006
|
-
this.conversation_id = await this.createConversation();
|
|
1007
1189
|
this.addMessage('assistant', this.getWelcomeMessage(this.user_info?.first_name || ''));
|
|
1008
|
-
this.last_message_count = 1;
|
|
1009
|
-
|
|
1010
1190
|
const theme = getClientTheme(this.client_id);
|
|
1011
1191
|
if (theme.suggestions && theme.suggestions.length > 0) this.showSuggestions(theme.suggestions);
|
|
1012
|
-
|
|
1013
|
-
this.polling_interval = setInterval(() => this.pollMessages(), 3000);
|
|
1192
|
+
// pas de polling — rien à poller tant que la conv n'est pas créée
|
|
1014
1193
|
}
|
|
1015
1194
|
|
|
1016
|
-
|
|
1195
|
+
_updateSubtitle(mode) {
|
|
1196
|
+
const m = this.getMessages(),
|
|
1197
|
+
subtitle = this.querySelector('#tp-subtitle');
|
|
1198
|
+
if (!subtitle) return;
|
|
1199
|
+
if (mode === 'agent') subtitle.innerHTML = `${icon('user', 11)} ${m.human_agent}`;
|
|
1200
|
+
else if (mode === 'closed') subtitle.innerHTML = `${icon('check', 11)} ${m.terminated}`;
|
|
1201
|
+
else subtitle.innerHTML = `${icon('bot', 11)} ${m.virtual}`;
|
|
1202
|
+
}
|
|
1017
1203
|
|
|
1018
1204
|
showGuestForm() {
|
|
1019
1205
|
const container = this.querySelector('#tp-messages');
|
|
1020
1206
|
if (!container) return;
|
|
1021
|
-
this.querySelector('#tp-
|
|
1207
|
+
this.querySelector('#tp-input-bar').style.display = 'none';
|
|
1022
1208
|
this.querySelector('#tp-agent-bar').style.display = 'none';
|
|
1023
|
-
|
|
1024
|
-
const
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
<
|
|
1032
|
-
<
|
|
1033
|
-
<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>
|
|
1034
|
-
<p id="tp-guest-error" style="margin:0;font-size:12px;color:#c0392b;display:none;"></p>
|
|
1209
|
+
this.showTabs(false);
|
|
1210
|
+
const m = this.getMessages();
|
|
1211
|
+
container.innerHTML = '';
|
|
1212
|
+
const wrap = document.createElement('div');
|
|
1213
|
+
wrap.id = 'tp-guest-wrap';
|
|
1214
|
+
wrap.className = 'tp-guest-wrap';
|
|
1215
|
+
wrap.innerHTML = `
|
|
1216
|
+
<div class="tp-guest-intro">
|
|
1217
|
+
<div class="tp-guest-intro-name">${m.guest_title}</div>
|
|
1218
|
+
<div class="tp-guest-intro-sub">${m.guest_sub}</div>
|
|
1035
1219
|
</div>
|
|
1220
|
+
<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>
|
|
1221
|
+
<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>
|
|
1222
|
+
<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>
|
|
1223
|
+
<p id="tp-guest-error" class="tp-guest-error"></p>
|
|
1224
|
+
<button id="tp-guest-submit" class="tp-guest-submit">${icon('msg_plus', 15, 'color:white')} ${m.guest_submit}</button>
|
|
1225
|
+
<div class="tp-guest-footer">${icon('lock', 11)} ${m.guest_secured}</div>
|
|
1036
1226
|
`;
|
|
1037
|
-
container.appendChild(
|
|
1227
|
+
container.appendChild(wrap);
|
|
1038
1228
|
this.querySelector('#tp-guest-submit').addEventListener('click', () => this.submitGuestForm());
|
|
1229
|
+
['#tp-guest-firstname', '#tp-guest-company', '#tp-guest-email'].forEach(sel => {
|
|
1230
|
+
this.querySelector(sel)?.addEventListener('keydown', e => {
|
|
1231
|
+
if (e.key === 'Enter') this.submitGuestForm();
|
|
1232
|
+
});
|
|
1233
|
+
});
|
|
1039
1234
|
}
|
|
1040
1235
|
|
|
1236
|
+
// ─── FIXED: conv créée au premier sendMessage, pas après le formulaire ────
|
|
1041
1237
|
async submitGuestForm() {
|
|
1042
1238
|
const first_name = this.querySelector('#tp-guest-firstname')?.value.trim();
|
|
1043
1239
|
const company_name = this.querySelector('#tp-guest-company')?.value.trim();
|
|
1044
1240
|
const email = this.querySelector('#tp-guest-email')?.value.trim();
|
|
1045
1241
|
const error_el = this.querySelector('#tp-guest-error');
|
|
1242
|
+
const m = this.getMessages();
|
|
1046
1243
|
if (!first_name || !company_name || !email) {
|
|
1047
|
-
error_el.textContent =
|
|
1244
|
+
error_el.textContent = m.guest_fill;
|
|
1048
1245
|
error_el.style.display = 'block';
|
|
1049
1246
|
return;
|
|
1050
1247
|
}
|
|
1051
1248
|
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
1052
|
-
error_el.textContent =
|
|
1249
|
+
error_el.textContent = m.guest_email_invalid;
|
|
1053
1250
|
error_el.style.display = 'block';
|
|
1054
1251
|
return;
|
|
1055
1252
|
}
|
|
@@ -1057,66 +1254,49 @@
|
|
|
1057
1254
|
this.user_info = { user_id: `guest-${email}`, first_name, last_name: '', company_name, email, language: 'EN', site_id: '' };
|
|
1058
1255
|
this.user_id = this.user_info.user_id;
|
|
1059
1256
|
|
|
1060
|
-
const
|
|
1061
|
-
if (
|
|
1257
|
+
const wrap = this.querySelector('#tp-guest-wrap');
|
|
1258
|
+
if (wrap) wrap.remove();
|
|
1062
1259
|
|
|
1063
1260
|
this.view = 'chat';
|
|
1064
|
-
this.conversation_id =
|
|
1065
|
-
|
|
1066
|
-
this.querySelector('#tp-chatbot-input-bar').style.display = 'flex';
|
|
1261
|
+
this.conversation_id = null; // pas encore créée — sera créée au premier sendMessage
|
|
1262
|
+
this.querySelector('#tp-input-bar').style.display = 'flex';
|
|
1067
1263
|
this.querySelector('#tp-agent-bar').style.display = 'none';
|
|
1068
|
-
this.querySelector('#tp-close-bar').style.display = '
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
container.style.background = '#f5f5f7';
|
|
1072
|
-
container.style.padding = '16px';
|
|
1073
|
-
|
|
1264
|
+
this.querySelector('#tp-close-bar').style.display = 'none'; // cachée tant que pas de conv
|
|
1265
|
+
this.querySelector('#tp-back-btn').style.display = 'flex';
|
|
1266
|
+
this.showTabs(false);
|
|
1074
1267
|
this.updateUILanguage();
|
|
1075
1268
|
this.addMessage('assistant', this.getWelcomeMessage(first_name));
|
|
1076
|
-
this.last_message_count = 1;
|
|
1077
|
-
|
|
1078
1269
|
const theme = getClientTheme(this.client_id);
|
|
1079
1270
|
if (theme.suggestions && theme.suggestions.length > 0) this.showSuggestions(theme.suggestions);
|
|
1080
|
-
|
|
1081
|
-
this.polling_interval = setInterval(() => this.pollMessages(), 3000);
|
|
1271
|
+
// pas de polling — rien à poller tant que la conv n'est pas créée
|
|
1082
1272
|
}
|
|
1083
1273
|
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
addMessage(role, content, message_id = null) {
|
|
1087
|
-
this.messages.push({ role, content, message_id, created_at: new Date().toISOString() });
|
|
1274
|
+
addMessage(role, content, message_id = null, source = null) {
|
|
1275
|
+
this.messages.push({ role, content, message_id, source, created_at: new Date().toISOString() });
|
|
1088
1276
|
const container = this.querySelector('#tp-messages');
|
|
1089
|
-
|
|
1277
|
+
const m = this.getMessages();
|
|
1090
1278
|
const role_label =
|
|
1091
1279
|
role === 'user'
|
|
1092
|
-
? `${icon('user', 11
|
|
1280
|
+
? `${icon('user', 11)} ${this.user_info?.first_name || 'You'}`
|
|
1093
1281
|
: role === 'agent'
|
|
1094
|
-
? `${icon('agent', 11
|
|
1282
|
+
? `${icon('agent', 11)} Agent`
|
|
1095
1283
|
: role === 'system'
|
|
1096
1284
|
? 'Info'
|
|
1097
|
-
: `${icon('bot', 11
|
|
1098
|
-
|
|
1285
|
+
: `${icon('bot', 11)} Maria`;
|
|
1099
1286
|
const msg_el = document.createElement('div');
|
|
1100
|
-
msg_el.className = `tp-
|
|
1287
|
+
msg_el.className = `tp-msg ${role}`;
|
|
1101
1288
|
const rendered_content =
|
|
1102
1289
|
role === 'assistant' || role === 'agent' ? (typeof marked !== 'undefined' ? marked.parse(content) : content) : content;
|
|
1103
|
-
|
|
1290
|
+
const now = formatTime(new Date().toISOString());
|
|
1104
1291
|
const feedback_html =
|
|
1105
1292
|
role === 'assistant' && message_id
|
|
1106
|
-
? `<div class="tp-feedback-bar" data-message-id="${message_id}"
|
|
1107
|
-
<button class="tp-feedback-btn" data-rating="positive" title="Helpful">${icon('helpful', 14)}</button>
|
|
1108
|
-
<button class="tp-feedback-btn" data-rating="negative" title="Not helpful">${icon('not_helpful', 14)}</button>
|
|
1109
|
-
</div>`
|
|
1293
|
+
? `<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>`
|
|
1110
1294
|
: '';
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
${feedback_html}
|
|
1117
|
-
</div>
|
|
1118
|
-
`;
|
|
1119
|
-
|
|
1295
|
+
if (role === 'system') {
|
|
1296
|
+
msg_el.innerHTML = `<div class="tp-bubble-msg">${rendered_content}</div>`;
|
|
1297
|
+
} else {
|
|
1298
|
+
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>`;
|
|
1299
|
+
}
|
|
1120
1300
|
if (role === 'assistant' && message_id) {
|
|
1121
1301
|
const bar = msg_el.querySelector('.tp-feedback-bar');
|
|
1122
1302
|
bar.querySelectorAll('.tp-feedback-btn').forEach(btn => {
|
|
@@ -1125,9 +1305,11 @@
|
|
|
1125
1305
|
bar.dataset.voted = '1';
|
|
1126
1306
|
const rating = btn.dataset.rating;
|
|
1127
1307
|
bar.querySelectorAll('.tp-feedback-btn').forEach(b => {
|
|
1128
|
-
b.
|
|
1308
|
+
b.classList.remove('voted-positive', 'voted-negative');
|
|
1309
|
+
b.style.opacity = b === btn ? '1' : '0.35';
|
|
1129
1310
|
b.style.cursor = 'default';
|
|
1130
1311
|
});
|
|
1312
|
+
btn.classList.add(rating === 'positive' ? 'voted-positive' : 'voted-negative');
|
|
1131
1313
|
try {
|
|
1132
1314
|
await fetch(`${this.api_url}/chat/conversations/${this.conversation_id}/feedback`, {
|
|
1133
1315
|
method: 'POST',
|
|
@@ -1138,19 +1320,17 @@
|
|
|
1138
1320
|
});
|
|
1139
1321
|
});
|
|
1140
1322
|
}
|
|
1141
|
-
|
|
1142
1323
|
container.appendChild(msg_el);
|
|
1143
1324
|
container.scrollTop = container.scrollHeight;
|
|
1144
|
-
|
|
1145
1325
|
if ((role === 'assistant' || role === 'agent') && this.sound_enabled) playSound();
|
|
1146
1326
|
}
|
|
1147
1327
|
|
|
1148
1328
|
showTyping() {
|
|
1149
1329
|
const container = this.querySelector('#tp-messages');
|
|
1150
1330
|
const typing = document.createElement('div');
|
|
1151
|
-
typing.className = 'tp-
|
|
1331
|
+
typing.className = 'tp-msg assistant';
|
|
1152
1332
|
typing.id = 'tp-typing';
|
|
1153
|
-
typing.innerHTML = `<div class="tp-
|
|
1333
|
+
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>`;
|
|
1154
1334
|
container.appendChild(typing);
|
|
1155
1335
|
container.scrollTop = container.scrollHeight;
|
|
1156
1336
|
}
|
|
@@ -1166,56 +1346,33 @@
|
|
|
1166
1346
|
clearInterval(this.polling_interval);
|
|
1167
1347
|
this.polling_interval = null;
|
|
1168
1348
|
}
|
|
1169
|
-
|
|
1170
1349
|
const m = this.getMessages();
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
this.querySelector('#tp-chatbot-input-bar').style.display = 'none';
|
|
1350
|
+
this._updateSubtitle('closed');
|
|
1351
|
+
this.querySelector('#tp-input-bar').style.display = 'none';
|
|
1175
1352
|
this.querySelector('#tp-agent-bar').style.display = 'none';
|
|
1176
1353
|
this.querySelector('#tp-close-bar').style.display = 'none';
|
|
1177
1354
|
const sugg_el = this.querySelector('#tp-suggestions');
|
|
1178
1355
|
if (sugg_el) sugg_el.remove();
|
|
1179
|
-
|
|
1180
1356
|
const container = this.querySelector('#tp-messages');
|
|
1181
|
-
const existing_banner =
|
|
1357
|
+
const existing_banner = container.querySelector('#tp-closed-banner');
|
|
1182
1358
|
if (existing_banner) {
|
|
1183
1359
|
if (existing_banner.querySelector('#tp-csat-block')) return;
|
|
1184
1360
|
existing_banner.remove();
|
|
1185
1361
|
}
|
|
1186
|
-
|
|
1187
|
-
const color = this.getThemeColor();
|
|
1188
|
-
const dark = this.shadeColor(color, -20);
|
|
1189
|
-
|
|
1362
|
+
const now = new Date().toLocaleDateString('fr-FR', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' });
|
|
1190
1363
|
const csat_html = existing_csat
|
|
1191
|
-
? `<div
|
|
1192
|
-
: `<div
|
|
1193
|
-
<div style="display:flex;gap:10px;justify-content:center;margin-bottom:14px;">
|
|
1194
|
-
<button id="tp-csat-positive" class="tp-csat-btn" style="border-color:#22c55e;color:#22c55e;">${icon('helpful', 20)}</button>
|
|
1195
|
-
<button id="tp-csat-negative" class="tp-csat-btn" style="border-color:#ef4444;color:#ef4444;">${icon('not_helpful', 20)}</button>
|
|
1196
|
-
</div>`;
|
|
1197
|
-
|
|
1364
|
+
? `<div class="tp-csat-thanks">${existing_csat === 'positive' ? m.csat_positive : m.csat_negative}</div>`
|
|
1365
|
+
: `<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>`;
|
|
1198
1366
|
const banner = document.createElement('div');
|
|
1199
1367
|
banner.id = 'tp-closed-banner';
|
|
1200
|
-
banner.
|
|
1201
|
-
|
|
1202
|
-
<div style="display:inline-flex;align-items:center;gap:6px;font-size:13px;color:#1a1a2e;font-weight:600;margin-bottom:10px;">
|
|
1203
|
-
${icon('check', 14)} ${m.closed}
|
|
1204
|
-
</div>
|
|
1205
|
-
<div id="tp-csat-block">${csat_html}</div>
|
|
1206
|
-
<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;">
|
|
1207
|
-
${icon('msg_plus', 15, 'color:white')} ${m.new_conv}
|
|
1208
|
-
</button>
|
|
1209
|
-
<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>
|
|
1210
|
-
</div>
|
|
1211
|
-
`;
|
|
1368
|
+
banner.className = 'tp-closed-banner';
|
|
1369
|
+
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>`;
|
|
1212
1370
|
container.appendChild(banner);
|
|
1213
1371
|
container.scrollTop = container.scrollHeight;
|
|
1214
|
-
|
|
1215
1372
|
if (!existing_csat) {
|
|
1216
1373
|
const submit_csat = async rating => {
|
|
1217
|
-
const
|
|
1218
|
-
if (!
|
|
1374
|
+
const block = this.querySelector('#tp-csat-block');
|
|
1375
|
+
if (!block) return;
|
|
1219
1376
|
try {
|
|
1220
1377
|
await fetch(`${this.api_url}/chat/conversations/${this.conversation_id}/csat`, {
|
|
1221
1378
|
method: 'POST',
|
|
@@ -1225,20 +1382,21 @@
|
|
|
1225
1382
|
} catch (e) {
|
|
1226
1383
|
console.error('CSAT error:', e);
|
|
1227
1384
|
}
|
|
1228
|
-
|
|
1385
|
+
block.innerHTML = `<div class="tp-csat-thanks">${rating === 'positive' ? m.csat_positive : m.csat_negative}</div>`;
|
|
1229
1386
|
};
|
|
1230
|
-
this.querySelector('#tp-csat-positive')
|
|
1231
|
-
this.querySelector('#tp-csat-negative')
|
|
1387
|
+
this.querySelector('#tp-csat-positive')?.addEventListener('click', () => submit_csat('positive'));
|
|
1388
|
+
this.querySelector('#tp-csat-negative')?.addEventListener('click', () => submit_csat('negative'));
|
|
1232
1389
|
}
|
|
1233
|
-
|
|
1234
|
-
this.querySelector('#tp-
|
|
1235
|
-
|
|
1390
|
+
this.querySelector('#tp-closed-new-btn').addEventListener('click', () => this.startNewConversation());
|
|
1391
|
+
this.querySelector('#tp-closed-hist-btn').addEventListener('click', () => {
|
|
1392
|
+
this.setTab('history');
|
|
1393
|
+
this.showConversationList();
|
|
1394
|
+
});
|
|
1236
1395
|
}
|
|
1237
1396
|
|
|
1238
|
-
// ───
|
|
1239
|
-
|
|
1397
|
+
// ─── FIXED: createConversation lazy au premier sendMessage ────────────────
|
|
1240
1398
|
async sendMessage() {
|
|
1241
|
-
if (this.is_closed
|
|
1399
|
+
if (this.is_closed) return;
|
|
1242
1400
|
const m = this.getMessages();
|
|
1243
1401
|
const input = this.querySelector('#tp-input');
|
|
1244
1402
|
const query = input.value.trim();
|
|
@@ -1250,10 +1408,22 @@
|
|
|
1250
1408
|
const user_messages = this.messages.filter(msg => msg.role === 'user').length;
|
|
1251
1409
|
if (user_messages >= 30) {
|
|
1252
1410
|
this.addMessage('system', m.limit_reached);
|
|
1253
|
-
this.querySelector('#tp-
|
|
1411
|
+
this.querySelector('#tp-input-bar').style.display = 'none';
|
|
1254
1412
|
return;
|
|
1255
1413
|
}
|
|
1256
1414
|
|
|
1415
|
+
// Créer la conv au premier message si pas encore créée
|
|
1416
|
+
if (!this.conversation_id) {
|
|
1417
|
+
try {
|
|
1418
|
+
this.conversation_id = await this.createConversation();
|
|
1419
|
+
this.querySelector('#tp-close-bar').style.display = 'block';
|
|
1420
|
+
this.polling_interval = setInterval(() => this.pollMessages(), 3000);
|
|
1421
|
+
} catch (e) {
|
|
1422
|
+
this.addMessage('system', m.error_occurred);
|
|
1423
|
+
return;
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1257
1427
|
input.value = '';
|
|
1258
1428
|
this.is_loading = true;
|
|
1259
1429
|
this.addMessage('user', query);
|
|
@@ -1276,14 +1446,10 @@
|
|
|
1276
1446
|
|
|
1277
1447
|
this.agent_mode = data.result.agent_mode;
|
|
1278
1448
|
const source = data.result.source || null;
|
|
1279
|
-
|
|
1280
|
-
if (subtitle)
|
|
1281
|
-
subtitle.innerHTML = this.agent_mode
|
|
1282
|
-
? `${icon('user', 12, 'margin-right:4px')} ${m.human_agent}`
|
|
1283
|
-
: `${icon('bot', 12, 'margin-right:4px')} ${m.virtual}`;
|
|
1449
|
+
this._updateSubtitle(this.agent_mode ? 'agent' : 'bot');
|
|
1284
1450
|
|
|
1285
1451
|
if (data.result.reply) {
|
|
1286
|
-
this.addMessage(this.agent_mode ? 'agent' : 'assistant', data.result.reply, data.result.message_id || null);
|
|
1452
|
+
this.addMessage(this.agent_mode ? 'agent' : 'assistant', data.result.reply, data.result.message_id || null, source);
|
|
1287
1453
|
this.last_message_count += 2;
|
|
1288
1454
|
}
|
|
1289
1455
|
|
|
@@ -1292,18 +1458,11 @@
|
|
|
1292
1458
|
const suggest_agent = data.result.suggest_agent || false;
|
|
1293
1459
|
|
|
1294
1460
|
if (agent_bar && !this.agent_requested && !this.agent_mode) {
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
source === 'no_match' ||
|
|
1300
|
-
source === 'out_of_scope' ||
|
|
1301
|
-
user_msg_count >= 5
|
|
1302
|
-
) {
|
|
1303
|
-
agent_bar.style.display = 'block';
|
|
1304
|
-
}
|
|
1461
|
+
const semantic_trigger = source === 'no_match' || source === 'out_of_scope';
|
|
1462
|
+
const clarif_count = this.messages.filter(msg => msg.role === 'assistant' && msg.source === 'clarification').length;
|
|
1463
|
+
const repeated_clarif = source === 'clarification' && clarif_count >= 3;
|
|
1464
|
+
if (semantic_trigger || repeated_clarif || (suggest_agent && user_msg_count >= 5)) agent_bar.style.display = 'block';
|
|
1305
1465
|
}
|
|
1306
|
-
|
|
1307
1466
|
if (suggest_agent && !this.agent_requested) {
|
|
1308
1467
|
this.addMessage('system', m.suggest_agent_escalation);
|
|
1309
1468
|
this.last_message_count += 1;
|
|
@@ -1312,7 +1471,6 @@
|
|
|
1312
1471
|
this.hideTyping();
|
|
1313
1472
|
this.addMessage('assistant', this.getMessages().error_occurred);
|
|
1314
1473
|
}
|
|
1315
|
-
|
|
1316
1474
|
this.is_loading = false;
|
|
1317
1475
|
}
|
|
1318
1476
|
|
|
@@ -1321,7 +1479,6 @@
|
|
|
1321
1479
|
this.agent_requested = true;
|
|
1322
1480
|
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
1323
1481
|
if (agent_bar) agent_bar.style.display = 'none';
|
|
1324
|
-
|
|
1325
1482
|
try {
|
|
1326
1483
|
const response = await fetch(`${this.api_url}/chat/conversations/${this.conversation_id}/request-agent`, {
|
|
1327
1484
|
method: 'POST',
|
|
@@ -1329,21 +1486,18 @@
|
|
|
1329
1486
|
body: JSON.stringify({ user_id: this.user_id }),
|
|
1330
1487
|
});
|
|
1331
1488
|
const data = await response.json();
|
|
1332
|
-
const m = this.getMessages()
|
|
1333
|
-
|
|
1334
|
-
|
|
1489
|
+
const m = this.getMessages(),
|
|
1490
|
+
status = data.result?.status;
|
|
1335
1491
|
let msg = '';
|
|
1336
1492
|
if (status === 'outside_hours') msg = m.outside_hours(data.result.next_opening);
|
|
1337
1493
|
else if (status === 'no_agents') msg = m.no_agents;
|
|
1338
1494
|
else if (status === 'waiting_agent') msg = m.waiting_agent;
|
|
1339
1495
|
else if (status === 'waiting_agent_already') msg = m.waiting_agent_already;
|
|
1340
1496
|
else if (status === 'agent') msg = m.agent_already;
|
|
1341
|
-
|
|
1342
1497
|
if (msg) {
|
|
1343
1498
|
this.addMessage('system', msg);
|
|
1344
1499
|
this.last_message_count += 1;
|
|
1345
1500
|
}
|
|
1346
|
-
|
|
1347
1501
|
if (status === 'no_agents' || status === 'outside_hours' || status === 'waiting_agent_already') {
|
|
1348
1502
|
this.agent_requested = false;
|
|
1349
1503
|
if (agent_bar) agent_bar.style.display = 'block';
|
|
@@ -1378,24 +1532,21 @@
|
|
|
1378
1532
|
const data = await response.json();
|
|
1379
1533
|
const conv = data.result?.conversation;
|
|
1380
1534
|
if (!conv) return;
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
const is_typing = conv.is_typing || false;
|
|
1535
|
+
const server_messages = conv.messages || [],
|
|
1536
|
+
server_status = conv.status,
|
|
1537
|
+
is_typing = conv.is_typing || false;
|
|
1385
1538
|
const m = this.getMessages();
|
|
1386
|
-
|
|
1387
1539
|
if (server_status === 'closed' && !this.is_closed) {
|
|
1388
1540
|
this.showClosed();
|
|
1389
1541
|
return;
|
|
1390
1542
|
}
|
|
1391
|
-
|
|
1392
1543
|
if (is_typing && server_status === 'agent') {
|
|
1393
1544
|
if (!this.querySelector('#tp-agent-typing')) {
|
|
1394
1545
|
const container = this.querySelector('#tp-messages');
|
|
1395
1546
|
const typing = document.createElement('div');
|
|
1396
|
-
typing.className = 'tp-
|
|
1547
|
+
typing.className = 'tp-msg agent';
|
|
1397
1548
|
typing.id = 'tp-agent-typing';
|
|
1398
|
-
typing.innerHTML = `<div class="tp-
|
|
1549
|
+
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>`;
|
|
1399
1550
|
container.appendChild(typing);
|
|
1400
1551
|
container.scrollTop = container.scrollHeight;
|
|
1401
1552
|
}
|
|
@@ -1403,13 +1554,12 @@
|
|
|
1403
1554
|
const typing_el = this.querySelector('#tp-agent-typing');
|
|
1404
1555
|
if (typing_el) typing_el.remove();
|
|
1405
1556
|
}
|
|
1406
|
-
|
|
1407
1557
|
if (server_messages.length > this.last_message_count) {
|
|
1408
1558
|
const new_messages = server_messages.slice(this.last_message_count);
|
|
1409
1559
|
new_messages.forEach(msg => {
|
|
1410
1560
|
if (msg.role === 'agent') {
|
|
1411
|
-
const
|
|
1412
|
-
if (
|
|
1561
|
+
const t = this.querySelector('#tp-agent-typing');
|
|
1562
|
+
if (t) t.remove();
|
|
1413
1563
|
this.addMessage('agent', msg.content, msg.message_id || null);
|
|
1414
1564
|
} else if (msg.role === 'assistant') {
|
|
1415
1565
|
this.addMessage('assistant', msg.content, msg.message_id || null);
|
|
@@ -1419,22 +1569,23 @@
|
|
|
1419
1569
|
});
|
|
1420
1570
|
this.last_message_count = server_messages.length;
|
|
1421
1571
|
}
|
|
1422
|
-
|
|
1423
1572
|
if (server_status === 'agent' && !this.agent_mode) {
|
|
1424
1573
|
this.agent_mode = true;
|
|
1425
|
-
|
|
1426
|
-
if (subtitle) subtitle.innerHTML = `${icon('user', 12, 'margin-right:4px')} ${m.human_agent}`;
|
|
1574
|
+
this._updateSubtitle('agent');
|
|
1427
1575
|
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
1428
1576
|
if (agent_bar) agent_bar.style.display = 'none';
|
|
1577
|
+
const close_bar = this.querySelector('#tp-close-bar');
|
|
1578
|
+
if (close_bar) close_bar.style.display = 'none';
|
|
1429
1579
|
this.addMessage('system', m.agent_taken);
|
|
1430
1580
|
this.last_message_count = server_messages.length;
|
|
1431
1581
|
} else if (server_status === 'bot' && this.agent_mode) {
|
|
1432
1582
|
this.agent_mode = false;
|
|
1433
1583
|
this.agent_requested = false;
|
|
1434
|
-
|
|
1435
|
-
if (subtitle) subtitle.innerHTML = `${icon('bot', 12, 'margin-right:4px')} ${m.virtual}`;
|
|
1584
|
+
this._updateSubtitle('bot');
|
|
1436
1585
|
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
1437
1586
|
if (agent_bar) agent_bar.style.display = 'block';
|
|
1587
|
+
const close_bar = this.querySelector('#tp-close-bar');
|
|
1588
|
+
if (close_bar) close_bar.style.display = 'block';
|
|
1438
1589
|
this.addMessage('system', m.agent_released);
|
|
1439
1590
|
this.last_message_count = server_messages.length;
|
|
1440
1591
|
}
|