@developpement/tp-chatbot-widget 0.0.1
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 +17 -0
- package/src/chatbot.css +305 -0
- package/src/chatbot.js +683 -0
package/package.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@developpement/tp-chatbot-widget",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Chatbot widget for TravelPlanet / Makitizy",
|
|
5
|
+
"main": "src/chatbot.js",
|
|
6
|
+
"files": [
|
|
7
|
+
"src/chatbot.js",
|
|
8
|
+
"src/chatbot.css"
|
|
9
|
+
],
|
|
10
|
+
"keywords": [
|
|
11
|
+
"chatbot",
|
|
12
|
+
"widget",
|
|
13
|
+
"makitizy",
|
|
14
|
+
"travelplanet"
|
|
15
|
+
],
|
|
16
|
+
"license": "ISC"
|
|
17
|
+
}
|
package/src/chatbot.css
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
.tp-chatbot-host {
|
|
2
|
+
position: fixed;
|
|
3
|
+
bottom: 24px;
|
|
4
|
+
right: 24px;
|
|
5
|
+
z-index: 9999;
|
|
6
|
+
font-family:
|
|
7
|
+
'Segoe UI',
|
|
8
|
+
system-ui,
|
|
9
|
+
-apple-system,
|
|
10
|
+
sans-serif;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.tp-chatbot-bubble {
|
|
14
|
+
width: 56px;
|
|
15
|
+
height: 56px;
|
|
16
|
+
border-radius: 50%;
|
|
17
|
+
background: linear-gradient(135deg, #7b1fa2, #9c27b0);
|
|
18
|
+
display: flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
justify-content: center;
|
|
21
|
+
cursor: pointer;
|
|
22
|
+
box-shadow: 0 4px 16px rgba(123, 31, 162, 0.4);
|
|
23
|
+
font-size: 24px;
|
|
24
|
+
transition: transform 0.2s;
|
|
25
|
+
margin-left: auto;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.tp-chatbot-bubble:hover {
|
|
29
|
+
transform: scale(1.1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.tp-chatbot-window {
|
|
33
|
+
position: absolute;
|
|
34
|
+
bottom: 70px;
|
|
35
|
+
right: 0;
|
|
36
|
+
width: 360px;
|
|
37
|
+
height: 500px;
|
|
38
|
+
background: white;
|
|
39
|
+
border-radius: 16px;
|
|
40
|
+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
|
|
41
|
+
display: flex;
|
|
42
|
+
flex-direction: column;
|
|
43
|
+
overflow: hidden;
|
|
44
|
+
opacity: 0;
|
|
45
|
+
transform: translateY(20px) scale(0.95);
|
|
46
|
+
pointer-events: none;
|
|
47
|
+
transition: all 0.25s ease;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.tp-chatbot-window.open {
|
|
51
|
+
opacity: 1;
|
|
52
|
+
transform: translateY(0) scale(1);
|
|
53
|
+
pointer-events: all;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.tp-chatbot-header {
|
|
57
|
+
background: linear-gradient(135deg, #7b1fa2, #9c27b0);
|
|
58
|
+
padding: 14px 16px;
|
|
59
|
+
display: flex;
|
|
60
|
+
align-items: center;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.tp-chatbot-avatar {
|
|
64
|
+
width: 36px;
|
|
65
|
+
height: 36px;
|
|
66
|
+
border-radius: 50%;
|
|
67
|
+
background: white;
|
|
68
|
+
display: flex;
|
|
69
|
+
align-items: center;
|
|
70
|
+
justify-content: center;
|
|
71
|
+
font-weight: 700;
|
|
72
|
+
color: #7b1fa2;
|
|
73
|
+
font-size: 16px;
|
|
74
|
+
margin-right: 10px;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.tp-chatbot-title {
|
|
78
|
+
color: white;
|
|
79
|
+
font-weight: 700;
|
|
80
|
+
font-size: 14px;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.tp-chatbot-subtitle {
|
|
84
|
+
color: rgba(255, 255, 255, 0.75);
|
|
85
|
+
font-size: 11px;
|
|
86
|
+
margin-top: 2px;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.tp-chatbot-messages {
|
|
90
|
+
flex: 1;
|
|
91
|
+
overflow-y: auto;
|
|
92
|
+
padding: 16px;
|
|
93
|
+
display: flex;
|
|
94
|
+
flex-direction: column;
|
|
95
|
+
gap: 10px;
|
|
96
|
+
background: #f5f5f7;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.tp-chatbot-welcome {
|
|
100
|
+
text-align: center;
|
|
101
|
+
color: #aaa;
|
|
102
|
+
font-size: 13px;
|
|
103
|
+
padding: 20px;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.tp-chatbot-message {
|
|
107
|
+
display: flex;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.tp-chatbot-message.user {
|
|
111
|
+
justify-content: flex-end;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.tp-chatbot-message.assistant,
|
|
115
|
+
.tp-chatbot-message.agent,
|
|
116
|
+
.tp-chatbot-message.system {
|
|
117
|
+
justify-content: flex-start;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.tp-chatbot-bubble-msg {
|
|
121
|
+
max-width: 80%;
|
|
122
|
+
padding: 10px 14px;
|
|
123
|
+
border-radius: 16px;
|
|
124
|
+
font-size: 13px;
|
|
125
|
+
line-height: 1.5;
|
|
126
|
+
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.tp-chatbot-message.user .tp-chatbot-bubble-msg {
|
|
130
|
+
background: linear-gradient(135deg, #7b1fa2, #9c27b0);
|
|
131
|
+
color: white;
|
|
132
|
+
border-radius: 16px 16px 4px 16px;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.tp-chatbot-message.assistant .tp-chatbot-bubble-msg {
|
|
136
|
+
background: white;
|
|
137
|
+
color: #1a1a2e;
|
|
138
|
+
border: 1px solid #ede8f5;
|
|
139
|
+
border-radius: 16px 16px 16px 4px;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.tp-chatbot-message.agent .tp-chatbot-bubble-msg {
|
|
143
|
+
background: #fff8e1;
|
|
144
|
+
color: #1a1a2e;
|
|
145
|
+
border: 1px solid #ffe082;
|
|
146
|
+
border-radius: 16px 16px 16px 4px;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.tp-chatbot-message.system .tp-chatbot-bubble-msg {
|
|
150
|
+
background: #e8f5e9;
|
|
151
|
+
color: #2e7d32;
|
|
152
|
+
border: 1px solid #c8e6c9;
|
|
153
|
+
border-radius: 12px;
|
|
154
|
+
font-size: 12px;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.tp-chatbot-role {
|
|
158
|
+
font-size: 10px;
|
|
159
|
+
font-weight: 600;
|
|
160
|
+
margin-bottom: 4px;
|
|
161
|
+
color: #7b1fa2;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.tp-chatbot-message.user .tp-chatbot-role {
|
|
165
|
+
color: rgba(255, 255, 255, 0.8);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.tp-chatbot-typing {
|
|
169
|
+
display: flex;
|
|
170
|
+
gap: 4px;
|
|
171
|
+
padding: 4px 0;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.tp-chatbot-typing span {
|
|
175
|
+
width: 8px;
|
|
176
|
+
height: 8px;
|
|
177
|
+
border-radius: 50%;
|
|
178
|
+
background: #7b1fa2;
|
|
179
|
+
animation: tp-typing 1.2s infinite;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.tp-chatbot-typing span:nth-child(2) {
|
|
183
|
+
animation-delay: 0.2s;
|
|
184
|
+
}
|
|
185
|
+
.tp-chatbot-typing span:nth-child(3) {
|
|
186
|
+
animation-delay: 0.4s;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
@keyframes tp-typing {
|
|
190
|
+
0%,
|
|
191
|
+
60%,
|
|
192
|
+
100% {
|
|
193
|
+
transform: translateY(0);
|
|
194
|
+
opacity: 0.4;
|
|
195
|
+
}
|
|
196
|
+
30% {
|
|
197
|
+
transform: translateY(-6px);
|
|
198
|
+
opacity: 1;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.tp-chatbot-agent-bar {
|
|
203
|
+
padding: 8px 16px;
|
|
204
|
+
border-top: 1px solid #ede8f5;
|
|
205
|
+
background: white;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.tp-chatbot-agent-btn {
|
|
209
|
+
width: 100%;
|
|
210
|
+
padding: 8px;
|
|
211
|
+
background: none;
|
|
212
|
+
border: 1.5px solid #7b1fa2;
|
|
213
|
+
border-radius: 8px;
|
|
214
|
+
color: #7b1fa2;
|
|
215
|
+
font-size: 13px;
|
|
216
|
+
font-weight: 600;
|
|
217
|
+
cursor: pointer;
|
|
218
|
+
transition: all 0.2s;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.tp-chatbot-agent-btn:hover {
|
|
222
|
+
background: #7b1fa2;
|
|
223
|
+
color: white;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.tp-chatbot-input-bar {
|
|
227
|
+
padding: 12px 16px;
|
|
228
|
+
border-top: 1px solid #ede8f5;
|
|
229
|
+
background: white;
|
|
230
|
+
display: flex;
|
|
231
|
+
gap: 8px;
|
|
232
|
+
align-items: flex-end;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.tp-chatbot-input {
|
|
236
|
+
flex: 1;
|
|
237
|
+
padding: 10px 14px;
|
|
238
|
+
border-radius: 10px;
|
|
239
|
+
border: 1.5px solid #ede8f5;
|
|
240
|
+
font-size: 13px;
|
|
241
|
+
resize: none;
|
|
242
|
+
font-family: inherit;
|
|
243
|
+
color: #1a1a2e;
|
|
244
|
+
background: #fafafa;
|
|
245
|
+
transition: border 0.2s;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.tp-chatbot-input:focus {
|
|
249
|
+
outline: none;
|
|
250
|
+
border-color: #7b1fa2;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.tp-chatbot-send {
|
|
254
|
+
width: 38px;
|
|
255
|
+
height: 38px;
|
|
256
|
+
border-radius: 50%;
|
|
257
|
+
background: linear-gradient(135deg, #7b1fa2, #9c27b0);
|
|
258
|
+
color: white;
|
|
259
|
+
border: none;
|
|
260
|
+
font-size: 16px;
|
|
261
|
+
cursor: pointer;
|
|
262
|
+
display: flex;
|
|
263
|
+
align-items: center;
|
|
264
|
+
justify-content: center;
|
|
265
|
+
transition: opacity 0.2s;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.tp-chatbot-send:disabled {
|
|
269
|
+
opacity: 0.4;
|
|
270
|
+
cursor: not-allowed;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.tp-chatbot-send:hover:not(:disabled) {
|
|
274
|
+
opacity: 0.85;
|
|
275
|
+
}
|
|
276
|
+
.tp-chatbot-markdown h1,
|
|
277
|
+
.tp-chatbot-markdown h2,
|
|
278
|
+
.tp-chatbot-markdown h3 {
|
|
279
|
+
font-size: 14px;
|
|
280
|
+
font-weight: 700;
|
|
281
|
+
margin: 10px 0 4px;
|
|
282
|
+
color: #1a1a2e;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.tp-chatbot-markdown p {
|
|
286
|
+
margin: 4px 0;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.tp-chatbot-markdown ul,
|
|
290
|
+
.tp-chatbot-markdown ol {
|
|
291
|
+
margin: 4px 0 4px 16px;
|
|
292
|
+
padding: 0;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.tp-chatbot-markdown li {
|
|
296
|
+
margin: 2px 0;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.tp-chatbot-markdown strong {
|
|
300
|
+
font-weight: 700;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.tp-chatbot-markdown em {
|
|
304
|
+
font-style: italic;
|
|
305
|
+
}
|
package/src/chatbot.js
ADDED
|
@@ -0,0 +1,683 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
function extractUserInfo(access_token) {
|
|
5
|
+
if (!access_token) return null;
|
|
6
|
+
try {
|
|
7
|
+
const payload = JSON.parse(atob(access_token.split('.')[1]));
|
|
8
|
+
return {
|
|
9
|
+
user_id: payload.userId || payload.sub,
|
|
10
|
+
first_name: payload.firstName || '',
|
|
11
|
+
last_name: payload.lastName || '',
|
|
12
|
+
company_name: payload.companyName || '',
|
|
13
|
+
language: payload.language || 'FR',
|
|
14
|
+
site_id: payload.siteId || '',
|
|
15
|
+
};
|
|
16
|
+
} catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
class TpChatbot extends HTMLElement {
|
|
22
|
+
constructor() {
|
|
23
|
+
super();
|
|
24
|
+
this.is_open = false;
|
|
25
|
+
this.is_loading = false;
|
|
26
|
+
this.messages = [];
|
|
27
|
+
this.conversation_id = null;
|
|
28
|
+
this.user_id = null;
|
|
29
|
+
this.agent_mode = false;
|
|
30
|
+
this.agent_requested = false;
|
|
31
|
+
this.last_message_count = 0;
|
|
32
|
+
this.polling_interval = null;
|
|
33
|
+
this.user_info = null;
|
|
34
|
+
this.is_closed = false;
|
|
35
|
+
this.view = 'list'; // 'list' | 'chat'
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
connectedCallback() {
|
|
39
|
+
this.access_token = this.getAttribute('access-token') || '';
|
|
40
|
+
this.api_url = this.getAttribute('api-url') || 'https://api.tst.travelplanet.click/chatbot/v1';
|
|
41
|
+
this.user_info = null;
|
|
42
|
+
this.conversation_id = null;
|
|
43
|
+
this.user_id = null;
|
|
44
|
+
this.last_message_count = 0;
|
|
45
|
+
this.polling_interval = null;
|
|
46
|
+
this.is_closed = false;
|
|
47
|
+
this.view = 'list';
|
|
48
|
+
|
|
49
|
+
this.render();
|
|
50
|
+
this.attachEvents();
|
|
51
|
+
|
|
52
|
+
if (this.access_token) {
|
|
53
|
+
this.user_info = extractUserInfo(this.access_token);
|
|
54
|
+
this.user_id = this.user_info.user_id;
|
|
55
|
+
this.applyTheme();
|
|
56
|
+
this.showConversationList();
|
|
57
|
+
} else {
|
|
58
|
+
this.showGuestForm();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
disconnectedCallback() {
|
|
63
|
+
if (this.polling_interval) clearInterval(this.polling_interval);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ─── Theme ────────────────────────────────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
getThemeColor() {
|
|
69
|
+
const site_id = this.user_info?.site_id || '';
|
|
70
|
+
return site_id.startsWith('I') ? '#97d700' : '#7b1fa2';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
shadeColor(hex, percent) {
|
|
74
|
+
const num = parseInt(hex.replace('#', ''), 16);
|
|
75
|
+
const r = Math.min(255, Math.max(0, (num >> 16) + percent));
|
|
76
|
+
const g = Math.min(255, Math.max(0, ((num >> 8) & 0xff) + percent));
|
|
77
|
+
const b = Math.min(255, Math.max(0, (num & 0xff) + percent));
|
|
78
|
+
return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
applyTheme() {
|
|
82
|
+
const color = this.getThemeColor();
|
|
83
|
+
const dark = this.shadeColor(color, -20);
|
|
84
|
+
const existing = document.getElementById('tp-chatbot-theme');
|
|
85
|
+
if (existing) existing.remove();
|
|
86
|
+
const style = document.createElement('style');
|
|
87
|
+
style.id = 'tp-chatbot-theme';
|
|
88
|
+
style.textContent = `
|
|
89
|
+
.tp-chatbot-bubble { background: linear-gradient(135deg, ${color}, ${dark}) !important; box-shadow: 0 4px 16px ${color}66 !important; }
|
|
90
|
+
.tp-chatbot-header { background: linear-gradient(135deg, ${color}, ${dark}) !important; }
|
|
91
|
+
.tp-chatbot-message.user .tp-chatbot-bubble-msg { background: linear-gradient(135deg, ${color}, ${dark}) !important; }
|
|
92
|
+
.tp-chatbot-avatar { color: ${color} !important; }
|
|
93
|
+
.tp-chatbot-role { color: ${color} !important; }
|
|
94
|
+
.tp-chatbot-message.user .tp-chatbot-role { color: rgba(255,255,255,0.8) !important; }
|
|
95
|
+
.tp-chatbot-input:focus { border-color: ${color} !important; }
|
|
96
|
+
.tp-chatbot-send { background: linear-gradient(135deg, ${color}, ${dark}) !important; }
|
|
97
|
+
.tp-chatbot-agent-btn { border-color: ${color} !important; color: ${color} !important; }
|
|
98
|
+
.tp-chatbot-agent-btn:hover { background: ${color} !important; color: white !important; }
|
|
99
|
+
.tp-chatbot-typing span { background: ${color} !important; }
|
|
100
|
+
#tp-new-conversation { background: linear-gradient(135deg, ${color}, ${dark}) !important; }
|
|
101
|
+
.tp-conv-item:hover { background: #f9f0ff !important; }
|
|
102
|
+
.tp-conv-new-btn { background: linear-gradient(135deg, ${color}, ${dark}) !important; }
|
|
103
|
+
`;
|
|
104
|
+
document.head.appendChild(style);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ─── Render ───────────────────────────────────────────────────────────────
|
|
108
|
+
|
|
109
|
+
render() {
|
|
110
|
+
this.innerHTML = `
|
|
111
|
+
<div class="tp-chatbot-host">
|
|
112
|
+
<div class="tp-chatbot-window" id="tp-window">
|
|
113
|
+
<div class="tp-chatbot-header" id="tp-header">
|
|
114
|
+
<div class="tp-chatbot-avatar">M</div>
|
|
115
|
+
<div style="flex:1">
|
|
116
|
+
<div class="tp-chatbot-title">Makitizy Support</div>
|
|
117
|
+
<div class="tp-chatbot-subtitle" id="tp-subtitle">🤖 Assistant virtuel</div>
|
|
118
|
+
</div>
|
|
119
|
+
<button id="tp-back-btn" style="display:none;background:none;border:none;color:white;font-size:18px;cursor:pointer;padding:4px 8px;">←</button>
|
|
120
|
+
</div>
|
|
121
|
+
<div class="tp-chatbot-messages" id="tp-messages"></div>
|
|
122
|
+
<div class="tp-chatbot-agent-bar" id="tp-agent-bar">
|
|
123
|
+
<button class="tp-chatbot-agent-btn" id="tp-agent-btn">👤 Parler à un agent</button>
|
|
124
|
+
</div>
|
|
125
|
+
<div class="tp-chatbot-input-bar" id="tp-chatbot-input-bar">
|
|
126
|
+
<textarea class="tp-chatbot-input" id="tp-input" placeholder="Écrivez votre message..." rows="1"></textarea>
|
|
127
|
+
<button class="tp-chatbot-send" id="tp-send">➤</button>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
<div class="tp-chatbot-bubble" id="tp-bubble">💬</div>
|
|
131
|
+
</div>
|
|
132
|
+
`;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
attachEvents() {
|
|
136
|
+
this.querySelector('#tp-bubble').addEventListener('click', () => this.toggleChat());
|
|
137
|
+
this.querySelector('#tp-send').addEventListener('click', () => this.sendMessage());
|
|
138
|
+
this.querySelector('#tp-agent-btn').addEventListener('click', () => this.requestAgent());
|
|
139
|
+
this.querySelector('#tp-back-btn').addEventListener('click', () => this.showConversationList());
|
|
140
|
+
this.querySelector('#tp-input').addEventListener('keydown', e => {
|
|
141
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
142
|
+
e.preventDefault();
|
|
143
|
+
this.sendMessage();
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
toggleChat() {
|
|
149
|
+
this.is_open = !this.is_open;
|
|
150
|
+
const window_el = this.querySelector('#tp-window');
|
|
151
|
+
const bubble = this.querySelector('#tp-bubble');
|
|
152
|
+
if (this.is_open) {
|
|
153
|
+
window_el.classList.add('open');
|
|
154
|
+
bubble.textContent = '✕';
|
|
155
|
+
} else {
|
|
156
|
+
window_el.classList.remove('open');
|
|
157
|
+
bubble.textContent = '💬';
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
getHeaders(content_type = false) {
|
|
162
|
+
const headers = {};
|
|
163
|
+
if (content_type) headers['Content-Type'] = 'application/json';
|
|
164
|
+
if (this.access_token) headers['Authorization'] = this.access_token;
|
|
165
|
+
return headers;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async createConversation() {
|
|
169
|
+
const response = await fetch(`${this.api_url}/chat/conversations`, {
|
|
170
|
+
method: 'POST',
|
|
171
|
+
headers: this.getHeaders(true),
|
|
172
|
+
body: JSON.stringify({ user_id: this.user_id, user_info: this.user_info }),
|
|
173
|
+
});
|
|
174
|
+
const data = await response.json();
|
|
175
|
+
return data.result?.conversation_id;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// ─── Conversation List ────────────────────────────────────────────────────
|
|
179
|
+
|
|
180
|
+
async showConversationList() {
|
|
181
|
+
this.view = 'list';
|
|
182
|
+
|
|
183
|
+
if (this.polling_interval) {
|
|
184
|
+
clearInterval(this.polling_interval);
|
|
185
|
+
this.polling_interval = null;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const subtitle = this.querySelector('#tp-subtitle');
|
|
189
|
+
if (subtitle) subtitle.textContent = '🤖 Assistant virtuel';
|
|
190
|
+
|
|
191
|
+
const back_btn = this.querySelector('#tp-back-btn');
|
|
192
|
+
if (back_btn) back_btn.style.display = 'none';
|
|
193
|
+
|
|
194
|
+
const input_bar = this.querySelector('#tp-chatbot-input-bar');
|
|
195
|
+
if (input_bar) input_bar.style.display = 'none';
|
|
196
|
+
|
|
197
|
+
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
198
|
+
if (agent_bar) agent_bar.style.display = 'none';
|
|
199
|
+
|
|
200
|
+
const container = this.querySelector('#tp-messages');
|
|
201
|
+
container.innerHTML = '<div style="padding:16px;text-align:center;color:#aaa;font-size:12px;">Chargement...</div>';
|
|
202
|
+
|
|
203
|
+
try {
|
|
204
|
+
const url = `${this.api_url}/chat/conversations?user_id=${encodeURIComponent(this.user_id)}&limit=5`;
|
|
205
|
+
const response = await fetch(url, { headers: this.getHeaders() });
|
|
206
|
+
const data = await response.json();
|
|
207
|
+
const conversations = data.result?.conversations || [];
|
|
208
|
+
|
|
209
|
+
const color = this.getThemeColor();
|
|
210
|
+
const dark = this.shadeColor(color, -20);
|
|
211
|
+
|
|
212
|
+
container.innerHTML = '';
|
|
213
|
+
|
|
214
|
+
// Header list
|
|
215
|
+
const header_el = document.createElement('div');
|
|
216
|
+
header_el.style.cssText = 'padding:16px;border-bottom:1px solid #ede8f5;';
|
|
217
|
+
header_el.innerHTML = `
|
|
218
|
+
<div style="font-size:13px;font-weight:700;color:#1a1a2e;margin-bottom:4px;">
|
|
219
|
+
Bonjour ${this.user_info?.first_name || ''} 👋
|
|
220
|
+
</div>
|
|
221
|
+
<div style="font-size:12px;color:#aaa;">Vos conversations récentes</div>
|
|
222
|
+
`;
|
|
223
|
+
container.appendChild(header_el);
|
|
224
|
+
|
|
225
|
+
// Conversation items
|
|
226
|
+
if (conversations.length === 0) {
|
|
227
|
+
const empty = document.createElement('div');
|
|
228
|
+
empty.style.cssText = 'padding:24px 16px;text-align:center;color:#aaa;font-size:13px;';
|
|
229
|
+
empty.textContent = 'Aucune conversation pour le moment.';
|
|
230
|
+
container.appendChild(empty);
|
|
231
|
+
} else {
|
|
232
|
+
conversations.forEach(conv => {
|
|
233
|
+
const item = document.createElement('div');
|
|
234
|
+
item.className = 'tp-conv-item';
|
|
235
|
+
item.style.cssText = 'padding:14px 16px;border-bottom:1px solid #f5f0fa;cursor:pointer;transition:background 0.15s;';
|
|
236
|
+
|
|
237
|
+
const is_closed = conv.status === 'closed';
|
|
238
|
+
const status_label = is_closed
|
|
239
|
+
? '✅ Fermée'
|
|
240
|
+
: conv.status === 'agent'
|
|
241
|
+
? '👤 Agent'
|
|
242
|
+
: conv.status === 'waiting_agent'
|
|
243
|
+
? '⏳ En attente'
|
|
244
|
+
: '🤖 Bot';
|
|
245
|
+
const status_color = is_closed ? '#9ca3af' : conv.status === 'waiting_agent' ? '#f59e0b' : color;
|
|
246
|
+
|
|
247
|
+
const date = new Date(conv.updated_at).toLocaleDateString('fr-FR', {
|
|
248
|
+
day: '2-digit',
|
|
249
|
+
month: '2-digit',
|
|
250
|
+
hour: '2-digit',
|
|
251
|
+
minute: '2-digit',
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
item.innerHTML = `
|
|
255
|
+
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:4px;">
|
|
256
|
+
<span style="font-size:13px;font-weight:600;color:${is_closed ? '#aaa' : '#1a1a2e'};">${date}</span>
|
|
257
|
+
<span style="font-size:10px;font-weight:700;color:${status_color};padding:2px 8px;border-radius:10px;background:${status_color}18;">${status_label}</span>
|
|
258
|
+
</div>
|
|
259
|
+
<div style="font-size:11px;color:#aaa;">${conv.message_count} message${conv.message_count > 1 ? 's' : ''}</div>
|
|
260
|
+
`;
|
|
261
|
+
|
|
262
|
+
item.addEventListener('click', () => this.openConversation(conv.conversation_id, is_closed));
|
|
263
|
+
container.appendChild(item);
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// New conversation button
|
|
268
|
+
const new_btn_wrap = document.createElement('div');
|
|
269
|
+
new_btn_wrap.style.cssText = 'padding:16px;';
|
|
270
|
+
new_btn_wrap.innerHTML = `
|
|
271
|
+
<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;">
|
|
272
|
+
💬 Nouvelle conversation
|
|
273
|
+
</button>
|
|
274
|
+
`;
|
|
275
|
+
new_btn_wrap.querySelector('button').addEventListener('click', () => this.startNewConversation());
|
|
276
|
+
container.appendChild(new_btn_wrap);
|
|
277
|
+
} catch (e) {
|
|
278
|
+
console.error('showConversationList error:', e);
|
|
279
|
+
container.innerHTML = '<div style="padding:16px;text-align:center;color:#ef4444;font-size:13px;">Erreur de chargement.</div>';
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async openConversation(conversation_id, is_closed = false) {
|
|
284
|
+
this.view = 'chat';
|
|
285
|
+
this.conversation_id = conversation_id;
|
|
286
|
+
this.messages = [];
|
|
287
|
+
this.last_message_count = 0;
|
|
288
|
+
this.is_closed = is_closed;
|
|
289
|
+
this.agent_mode = false;
|
|
290
|
+
this.agent_requested = false;
|
|
291
|
+
|
|
292
|
+
const back_btn = this.querySelector('#tp-back-btn');
|
|
293
|
+
if (back_btn) back_btn.style.display = 'block';
|
|
294
|
+
|
|
295
|
+
const container = this.querySelector('#tp-messages');
|
|
296
|
+
container.innerHTML = '';
|
|
297
|
+
|
|
298
|
+
const input_bar = this.querySelector('#tp-chatbot-input-bar');
|
|
299
|
+
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
300
|
+
|
|
301
|
+
try {
|
|
302
|
+
const url = `${this.api_url}/chat/conversations/${conversation_id}?user_id=${encodeURIComponent(this.user_id)}`;
|
|
303
|
+
const response = await fetch(url, { headers: this.getHeaders() });
|
|
304
|
+
const data = await response.json();
|
|
305
|
+
const conv = data.result?.conversation;
|
|
306
|
+
if (!conv) return;
|
|
307
|
+
|
|
308
|
+
conv.messages.forEach(msg => {
|
|
309
|
+
if (['user', 'assistant', 'agent', 'system'].includes(msg.role)) {
|
|
310
|
+
this.addMessage(msg.role, msg.content);
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
this.last_message_count = conv.messages.length;
|
|
315
|
+
|
|
316
|
+
if (conv.status === 'closed') {
|
|
317
|
+
this.showClosed();
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (conv.status === 'agent') {
|
|
322
|
+
this.agent_mode = true;
|
|
323
|
+
const subtitle = this.querySelector('#tp-subtitle');
|
|
324
|
+
if (subtitle) subtitle.textContent = '👤 Agent humain';
|
|
325
|
+
if (agent_bar) agent_bar.style.display = 'none';
|
|
326
|
+
if (input_bar) input_bar.style.display = 'flex';
|
|
327
|
+
} else {
|
|
328
|
+
if (input_bar) input_bar.style.display = 'flex';
|
|
329
|
+
if (agent_bar) agent_bar.style.display = 'block';
|
|
330
|
+
const subtitle = this.querySelector('#tp-subtitle');
|
|
331
|
+
if (subtitle) subtitle.textContent = '🤖 Assistant virtuel';
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
this.polling_interval = setInterval(() => this.pollMessages(), 3000);
|
|
335
|
+
} catch (e) {
|
|
336
|
+
console.error('openConversation error:', e);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
async startNewConversation() {
|
|
341
|
+
this.view = 'chat';
|
|
342
|
+
this.is_closed = false;
|
|
343
|
+
this.agent_mode = false;
|
|
344
|
+
this.agent_requested = false;
|
|
345
|
+
this.messages = [];
|
|
346
|
+
this.last_message_count = 0;
|
|
347
|
+
this.conversation_id = null;
|
|
348
|
+
|
|
349
|
+
const back_btn = this.querySelector('#tp-back-btn');
|
|
350
|
+
if (back_btn) back_btn.style.display = 'block';
|
|
351
|
+
|
|
352
|
+
const container = this.querySelector('#tp-messages');
|
|
353
|
+
container.innerHTML = '';
|
|
354
|
+
|
|
355
|
+
const subtitle = this.querySelector('#tp-subtitle');
|
|
356
|
+
if (subtitle) subtitle.textContent = '🤖 Assistant virtuel';
|
|
357
|
+
|
|
358
|
+
const input_bar = this.querySelector('#tp-chatbot-input-bar');
|
|
359
|
+
if (input_bar) input_bar.style.display = 'flex';
|
|
360
|
+
|
|
361
|
+
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
362
|
+
if (agent_bar) agent_bar.style.display = 'block';
|
|
363
|
+
|
|
364
|
+
this.conversation_id = await this.createConversation();
|
|
365
|
+
this.addMessage('assistant', `Bonjour ${this.user_info?.first_name || ''} ! Comment puis-je vous aider ?`);
|
|
366
|
+
this.polling_interval = setInterval(() => this.pollMessages(), 3000);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// ─── Guest form ───────────────────────────────────────────────────────────
|
|
370
|
+
|
|
371
|
+
showGuestForm() {
|
|
372
|
+
const container = this.querySelector('#tp-messages');
|
|
373
|
+
if (!container) return;
|
|
374
|
+
|
|
375
|
+
const input_bar = this.querySelector('#tp-chatbot-input-bar');
|
|
376
|
+
if (input_bar) input_bar.style.display = 'none';
|
|
377
|
+
|
|
378
|
+
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
379
|
+
if (agent_bar) agent_bar.style.display = 'none';
|
|
380
|
+
|
|
381
|
+
const color = this.getThemeColor();
|
|
382
|
+
const dark = this.shadeColor(color, -20);
|
|
383
|
+
|
|
384
|
+
const form_el = document.createElement('div');
|
|
385
|
+
form_el.id = 'tp-guest-form';
|
|
386
|
+
form_el.innerHTML = `
|
|
387
|
+
<div style="padding: 16px; display: flex; flex-direction: column; gap: 12px;">
|
|
388
|
+
<p style="margin: 0; font-size: 13px; color: #1a1a2e; font-weight: 600;">Avant de commencer, merci de vous identifier :</p>
|
|
389
|
+
<input id="tp-guest-firstname" type="text" placeholder="Prénom *" style="padding: 10px 14px; border-radius: 8px; border: 1.5px solid #ede8f5; font-size: 13px; font-family: inherit; outline: none;" />
|
|
390
|
+
<input id="tp-guest-company" type="text" placeholder="Nom d'entreprise *" style="padding: 10px 14px; border-radius: 8px; border: 1.5px solid #ede8f5; font-size: 13px; font-family: inherit; outline: none;" />
|
|
391
|
+
<input id="tp-guest-email" type="email" placeholder="Email *" style="padding: 10px 14px; border-radius: 8px; border: 1.5px solid #ede8f5; font-size: 13px; font-family: inherit; outline: none;" />
|
|
392
|
+
<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;">
|
|
393
|
+
Commencer la discussion →
|
|
394
|
+
</button>
|
|
395
|
+
<p id="tp-guest-error" style="margin: 0; font-size: 12px; color: #c0392b; display: none;"></p>
|
|
396
|
+
</div>
|
|
397
|
+
`;
|
|
398
|
+
|
|
399
|
+
container.appendChild(form_el);
|
|
400
|
+
this.querySelector('#tp-guest-submit').addEventListener('click', () => this.submitGuestForm());
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
async submitGuestForm() {
|
|
404
|
+
const first_name = this.querySelector('#tp-guest-firstname')?.value.trim();
|
|
405
|
+
const company_name = this.querySelector('#tp-guest-company')?.value.trim();
|
|
406
|
+
const email = this.querySelector('#tp-guest-email')?.value.trim();
|
|
407
|
+
const error_el = this.querySelector('#tp-guest-error');
|
|
408
|
+
|
|
409
|
+
if (!first_name || !company_name || !email) {
|
|
410
|
+
error_el.textContent = 'Veuillez remplir tous les champs.';
|
|
411
|
+
error_el.style.display = 'block';
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const email_valid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
|
416
|
+
if (!email_valid) {
|
|
417
|
+
error_el.textContent = 'Veuillez entrer un email valide.';
|
|
418
|
+
error_el.style.display = 'block';
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
this.user_info = { user_id: `guest-${email}`, first_name, last_name: '', company_name, email, language: 'FR', site_id: '' };
|
|
423
|
+
this.user_id = this.user_info.user_id;
|
|
424
|
+
this.applyTheme();
|
|
425
|
+
|
|
426
|
+
const form_el = this.querySelector('#tp-guest-form');
|
|
427
|
+
if (form_el) form_el.remove();
|
|
428
|
+
|
|
429
|
+
this.conversation_id = await this.createConversation();
|
|
430
|
+
|
|
431
|
+
const input_bar = this.querySelector('#tp-chatbot-input-bar');
|
|
432
|
+
if (input_bar) input_bar.style.display = 'flex';
|
|
433
|
+
|
|
434
|
+
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
435
|
+
if (agent_bar) agent_bar.style.display = 'block';
|
|
436
|
+
|
|
437
|
+
this.addMessage('assistant', `Bonjour ${first_name} ! Comment puis-je vous aider aujourd'hui ?`);
|
|
438
|
+
this.polling_interval = setInterval(() => this.pollMessages(), 3000);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// ─── Messages ─────────────────────────────────────────────────────────────
|
|
442
|
+
|
|
443
|
+
addMessage(role, content) {
|
|
444
|
+
this.messages.push({ role, content, created_at: new Date().toISOString() });
|
|
445
|
+
const container = this.querySelector('#tp-messages');
|
|
446
|
+
|
|
447
|
+
const role_label =
|
|
448
|
+
role === 'user'
|
|
449
|
+
? `👤 ${this.user_info?.first_name || 'Vous'}`
|
|
450
|
+
: role === 'agent'
|
|
451
|
+
? '👨💼 Agent'
|
|
452
|
+
: role === 'system'
|
|
453
|
+
? 'ℹ️ Info'
|
|
454
|
+
: '🤖 Assistant';
|
|
455
|
+
|
|
456
|
+
const msg_el = document.createElement('div');
|
|
457
|
+
msg_el.className = `tp-chatbot-message ${role}`;
|
|
458
|
+
const rendered_content =
|
|
459
|
+
role === 'assistant' || role === 'agent' ? (typeof marked !== 'undefined' ? marked.parse(content) : content) : content;
|
|
460
|
+
|
|
461
|
+
msg_el.innerHTML = `
|
|
462
|
+
<div class="tp-chatbot-bubble-msg">
|
|
463
|
+
<div class="tp-chatbot-role">${role_label}</div>
|
|
464
|
+
<div class="tp-chatbot-content tp-chatbot-markdown">${rendered_content}</div>
|
|
465
|
+
</div>
|
|
466
|
+
`;
|
|
467
|
+
container.appendChild(msg_el);
|
|
468
|
+
container.scrollTop = container.scrollHeight;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
showTyping() {
|
|
472
|
+
const container = this.querySelector('#tp-messages');
|
|
473
|
+
const typing = document.createElement('div');
|
|
474
|
+
typing.className = 'tp-chatbot-message assistant';
|
|
475
|
+
typing.id = 'tp-typing';
|
|
476
|
+
typing.innerHTML = `<div class="tp-chatbot-bubble-msg"><div class="tp-chatbot-typing"><span></span><span></span><span></span></div></div>`;
|
|
477
|
+
container.appendChild(typing);
|
|
478
|
+
container.scrollTop = container.scrollHeight;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
hideTyping() {
|
|
482
|
+
const typing = this.querySelector('#tp-typing');
|
|
483
|
+
if (typing) typing.remove();
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
showClosed() {
|
|
487
|
+
this.is_closed = true;
|
|
488
|
+
|
|
489
|
+
if (this.polling_interval) {
|
|
490
|
+
clearInterval(this.polling_interval);
|
|
491
|
+
this.polling_interval = null;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
const subtitle = this.querySelector('#tp-subtitle');
|
|
495
|
+
if (subtitle) subtitle.textContent = '✅ Conversation terminée';
|
|
496
|
+
|
|
497
|
+
const input_bar = this.querySelector('#tp-chatbot-input-bar');
|
|
498
|
+
if (input_bar) input_bar.style.display = 'none';
|
|
499
|
+
|
|
500
|
+
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
501
|
+
if (agent_bar) agent_bar.style.display = 'none';
|
|
502
|
+
|
|
503
|
+
const container = this.querySelector('#tp-messages');
|
|
504
|
+
if (this.querySelector('#tp-closed-banner')) return;
|
|
505
|
+
|
|
506
|
+
const color = this.getThemeColor();
|
|
507
|
+
const dark = this.shadeColor(color, -20);
|
|
508
|
+
|
|
509
|
+
const banner = document.createElement('div');
|
|
510
|
+
banner.id = 'tp-closed-banner';
|
|
511
|
+
banner.innerHTML = `
|
|
512
|
+
<div style="margin:16px;padding:16px;background:#fef2f2;border:1px solid #fecaca;border-radius:10px;text-align:center;">
|
|
513
|
+
<div style="font-size:13px;color:#ef4444;font-weight:600;margin-bottom:12px;">✅ Cette conversation a été clôturée par un agent.</div>
|
|
514
|
+
<button id="tp-new-conversation" style="padding:10px 20px;background:linear-gradient(135deg,${color},${dark});color:white;border:none;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;margin-bottom:8px;width:100%;">
|
|
515
|
+
💬 Nouvelle conversation
|
|
516
|
+
</button>
|
|
517
|
+
<button id="tp-back-to-list" style="padding:8px 20px;background:none;color:#7B1FA2;border:1px solid #ede8f5;border-radius:8px;font-size:12px;font-weight:600;cursor:pointer;width:100%;">
|
|
518
|
+
← Voir toutes les conversations
|
|
519
|
+
</button>
|
|
520
|
+
</div>
|
|
521
|
+
`;
|
|
522
|
+
container.appendChild(banner);
|
|
523
|
+
container.scrollTop = container.scrollHeight;
|
|
524
|
+
|
|
525
|
+
this.querySelector('#tp-new-conversation').addEventListener('click', () => this.startNewConversation());
|
|
526
|
+
this.querySelector('#tp-back-to-list').addEventListener('click', () => this.showConversationList());
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// ─── Send / Poll ──────────────────────────────────────────────────────────
|
|
530
|
+
|
|
531
|
+
async sendMessage() {
|
|
532
|
+
if (this.is_closed || !this.conversation_id) return;
|
|
533
|
+
const input = this.querySelector('#tp-input');
|
|
534
|
+
const query = input.value.trim();
|
|
535
|
+
if (!query || this.is_loading) return;
|
|
536
|
+
|
|
537
|
+
const user_messages = this.messages.filter(m => m.role === 'user').length;
|
|
538
|
+
if (user_messages >= 30) {
|
|
539
|
+
this.addMessage(
|
|
540
|
+
'system',
|
|
541
|
+
'Vous avez atteint la limite de 30 messages. Veuillez contacter un agent ou écrire à support@travelplanet.com.'
|
|
542
|
+
);
|
|
543
|
+
const input_bar = this.querySelector('#tp-chatbot-input-bar');
|
|
544
|
+
if (input_bar) input_bar.style.display = 'none';
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
input.value = '';
|
|
549
|
+
this.is_loading = true;
|
|
550
|
+
this.addMessage('user', query);
|
|
551
|
+
this.showTyping();
|
|
552
|
+
|
|
553
|
+
try {
|
|
554
|
+
const response = await fetch(`${this.api_url}/chat/conversations/${this.conversation_id}/messages`, {
|
|
555
|
+
method: 'POST',
|
|
556
|
+
headers: this.getHeaders(true),
|
|
557
|
+
body: JSON.stringify({ query, user_id: this.user_id, user_info: this.user_info }),
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
const data = await response.json();
|
|
561
|
+
this.hideTyping();
|
|
562
|
+
|
|
563
|
+
if (response.status === 429) {
|
|
564
|
+
this.addMessage('system', 'Vous avez atteint la limite de messages. Veuillez démarrer une nouvelle conversation.');
|
|
565
|
+
const input_bar = this.querySelector('#tp-chatbot-input-bar');
|
|
566
|
+
if (input_bar) input_bar.style.display = 'none';
|
|
567
|
+
this.is_loading = false;
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
this.agent_mode = data.result.agent_mode;
|
|
572
|
+
const subtitle = this.querySelector('#tp-subtitle');
|
|
573
|
+
if (subtitle) subtitle.textContent = this.agent_mode ? '👤 Agent humain' : '🤖 Assistant virtuel';
|
|
574
|
+
|
|
575
|
+
if (data.result.reply) {
|
|
576
|
+
this.addMessage(this.agent_mode ? 'agent' : 'assistant', data.result.reply);
|
|
577
|
+
}
|
|
578
|
+
} catch {
|
|
579
|
+
this.hideTyping();
|
|
580
|
+
this.addMessage('assistant', 'Une erreur est survenue. Veuillez réessayer.');
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
this.is_loading = false;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
async requestAgent() {
|
|
587
|
+
if (!this.conversation_id) return;
|
|
588
|
+
this.agent_requested = true;
|
|
589
|
+
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
590
|
+
if (agent_bar) agent_bar.style.display = 'none';
|
|
591
|
+
|
|
592
|
+
try {
|
|
593
|
+
const response = await fetch(`${this.api_url}/chat/conversations/${this.conversation_id}/request-agent`, {
|
|
594
|
+
method: 'POST',
|
|
595
|
+
headers: this.getHeaders(true),
|
|
596
|
+
body: JSON.stringify({ user_id: this.user_id }),
|
|
597
|
+
});
|
|
598
|
+
const data = await response.json();
|
|
599
|
+
this.addMessage('system', data.result.message);
|
|
600
|
+
|
|
601
|
+
if (data.result.status === 'no_agents' || data.result.status === 'outside_hours') {
|
|
602
|
+
this.agent_requested = false;
|
|
603
|
+
if (agent_bar) agent_bar.style.display = 'block';
|
|
604
|
+
}
|
|
605
|
+
} catch {
|
|
606
|
+
this.agent_requested = false;
|
|
607
|
+
if (agent_bar) agent_bar.style.display = 'block';
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
async pollMessages() {
|
|
612
|
+
if (!this.conversation_id || this.is_closed || this.view !== 'chat') return;
|
|
613
|
+
try {
|
|
614
|
+
const url = `${this.api_url}/chat/conversations/${this.conversation_id}?user_id=${encodeURIComponent(this.user_id)}`;
|
|
615
|
+
const response = await fetch(url, { headers: this.getHeaders() });
|
|
616
|
+
if (!response.ok) return;
|
|
617
|
+
|
|
618
|
+
const data = await response.json();
|
|
619
|
+
const conv = data.result?.conversation;
|
|
620
|
+
if (!conv) return;
|
|
621
|
+
|
|
622
|
+
const server_messages = conv.messages || [];
|
|
623
|
+
const server_status = conv.status;
|
|
624
|
+
const is_typing = conv.is_typing || false;
|
|
625
|
+
|
|
626
|
+
if (server_status === 'closed' && !this.is_closed) {
|
|
627
|
+
this.showClosed();
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
if (is_typing && server_status === 'agent') {
|
|
632
|
+
if (!this.querySelector('#tp-agent-typing')) {
|
|
633
|
+
const container = this.querySelector('#tp-messages');
|
|
634
|
+
const typing = document.createElement('div');
|
|
635
|
+
typing.className = 'tp-chatbot-message agent';
|
|
636
|
+
typing.id = 'tp-agent-typing';
|
|
637
|
+
typing.innerHTML = `<div class="tp-chatbot-bubble-msg"><div class="tp-chatbot-role">👨💼 Agent</div><div class="tp-chatbot-typing"><span></span><span></span><span></span></div></div>`;
|
|
638
|
+
container.appendChild(typing);
|
|
639
|
+
container.scrollTop = container.scrollHeight;
|
|
640
|
+
}
|
|
641
|
+
} else {
|
|
642
|
+
const typing_el = this.querySelector('#tp-agent-typing');
|
|
643
|
+
if (typing_el) typing_el.remove();
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
if (server_messages.length > this.last_message_count) {
|
|
647
|
+
const new_messages = server_messages.slice(this.last_message_count);
|
|
648
|
+
new_messages.forEach(msg => {
|
|
649
|
+
if (msg.role === 'agent') {
|
|
650
|
+
const typing_el = this.querySelector('#tp-agent-typing');
|
|
651
|
+
if (typing_el) typing_el.remove();
|
|
652
|
+
this.addMessage('agent', msg.content);
|
|
653
|
+
}
|
|
654
|
+
});
|
|
655
|
+
this.last_message_count = server_messages.length;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
if (server_status === 'agent' && !this.agent_mode) {
|
|
659
|
+
this.agent_mode = true;
|
|
660
|
+
const subtitle = this.querySelector('#tp-subtitle');
|
|
661
|
+
if (subtitle) subtitle.textContent = '👤 Agent humain';
|
|
662
|
+
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
663
|
+
if (agent_bar) agent_bar.style.display = 'none';
|
|
664
|
+
this.addMessage('system', 'Un agent a pris en charge votre conversation. Vous pouvez lui écrire directement.');
|
|
665
|
+
this.last_message_count = server_messages.length;
|
|
666
|
+
} else if (server_status === 'bot' && this.agent_mode) {
|
|
667
|
+
this.agent_mode = false;
|
|
668
|
+
this.agent_requested = false;
|
|
669
|
+
const subtitle = this.querySelector('#tp-subtitle');
|
|
670
|
+
if (subtitle) subtitle.textContent = '🤖 Assistant virtuel';
|
|
671
|
+
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
672
|
+
if (agent_bar) agent_bar.style.display = 'block';
|
|
673
|
+
this.addMessage('system', 'Notre assistant virtuel reprend la conversation. Comment puis-je vous aider ?');
|
|
674
|
+
this.last_message_count = server_messages.length;
|
|
675
|
+
}
|
|
676
|
+
} catch (e) {
|
|
677
|
+
console.error('pollMessages error:', e);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
customElements.define('tp-chatbot', TpChatbot);
|
|
683
|
+
})();
|