@ihoomanai/chat-widget 2.4.0 → 2.5.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/README.md +28 -8
- package/dist/index.cjs.js +209 -18
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +209 -18
- package/dist/index.esm.js.map +1 -1
- package/dist/index.esm.min.js +1 -1
- package/dist/index.esm.min.js.map +1 -1
- package/dist/index.umd.js +209 -18
- package/dist/index.umd.js.map +1 -1
- package/dist/index.umd.min.js +1 -1
- package/dist/index.umd.min.js.map +1 -1
- package/dist/widget.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/widget.ts +247 -18
package/src/widget.ts
CHANGED
|
@@ -72,6 +72,9 @@ const icons = {
|
|
|
72
72
|
bot: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="11" width="18" height="10" rx="2"></rect><circle cx="12" cy="5" r="2"></circle><path d="M12 7v4"></path></svg>`,
|
|
73
73
|
agent: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle></svg>`,
|
|
74
74
|
ticket: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><line x1="10" y1="9" x2="8" y2="9"></line></svg>`,
|
|
75
|
+
history: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>`,
|
|
76
|
+
back: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="19" y1="12" x2="5" y2="12"></line><polyline points="12 19 5 12 12 5"></polyline></svg>`,
|
|
77
|
+
plus: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg>`,
|
|
75
78
|
};
|
|
76
79
|
|
|
77
80
|
/**
|
|
@@ -90,7 +93,7 @@ let state: WidgetState = {
|
|
|
90
93
|
/**
|
|
91
94
|
* Current view state
|
|
92
95
|
*/
|
|
93
|
-
type WidgetView = 'chat' | 'ticket';
|
|
96
|
+
type WidgetView = 'chat' | 'ticket' | 'history';
|
|
94
97
|
let currentView: WidgetView = 'chat';
|
|
95
98
|
let isLiveAgentMode = false;
|
|
96
99
|
|
|
@@ -104,6 +107,9 @@ interface WidgetElements {
|
|
|
104
107
|
window?: HTMLElement;
|
|
105
108
|
chatView?: HTMLElement;
|
|
106
109
|
ticketView?: HTMLElement;
|
|
110
|
+
historyView?: HTMLElement;
|
|
111
|
+
historyList?: HTMLElement;
|
|
112
|
+
historyNewBtn?: HTMLButtonElement;
|
|
107
113
|
messages?: HTMLElement;
|
|
108
114
|
input?: HTMLTextAreaElement;
|
|
109
115
|
sendBtn?: HTMLButtonElement;
|
|
@@ -129,6 +135,7 @@ let ws: WebSocket | null = null;
|
|
|
129
135
|
let pollInterval: ReturnType<typeof setInterval> | null = null;
|
|
130
136
|
let reconnectAttempts = 0;
|
|
131
137
|
const maxReconnectAttempts = 5;
|
|
138
|
+
let intentionalDisconnect = false; // Flag to prevent reconnection on intentional close
|
|
132
139
|
|
|
133
140
|
|
|
134
141
|
// ============================================================================
|
|
@@ -257,9 +264,10 @@ function generateStyles(): string {
|
|
|
257
264
|
.ihooman-toggle:active { transform: scale(0.95); }
|
|
258
265
|
.ihooman-toggle::before { content: ''; position: absolute; inset: 0; background: linear-gradient(to bottom, rgba(255,255,255,0.2), transparent); border-radius: 50%; }
|
|
259
266
|
.ihooman-toggle svg { width: 26px; height: 26px; color: white; transition: transform 0.3s ease, opacity 0.2s ease; position: relative; z-index: 1; }
|
|
260
|
-
.ihooman-toggle
|
|
261
|
-
.ihooman-toggle
|
|
262
|
-
.ihooman-toggle
|
|
267
|
+
.ihooman-toggle .chat-icon { display: flex; align-items: center; justify-content: center; transition: transform 0.3s ease, opacity 0.2s ease; }
|
|
268
|
+
.ihooman-toggle .close-icon { position: absolute; display: flex; align-items: center; justify-content: center; transform: rotate(-90deg) scale(0); opacity: 0; transition: transform 0.3s ease, opacity 0.2s ease; }
|
|
269
|
+
.ihooman-toggle.open .chat-icon { transform: rotate(90deg) scale(0); opacity: 0; }
|
|
270
|
+
.ihooman-toggle.open .close-icon { transform: rotate(0) scale(1); opacity: 1; }
|
|
263
271
|
.ihooman-pulse { position: absolute; inset: 0; border-radius: 50%; background: ${primaryColor}; animation: ihooman-pulse 2s ease-out infinite; }
|
|
264
272
|
@keyframes ihooman-pulse { 0% { transform: scale(1); opacity: 0.5; } 100% { transform: scale(1.6); opacity: 0; } }
|
|
265
273
|
.ihooman-toggle.open .ihooman-pulse { display: none; }
|
|
@@ -280,7 +288,7 @@ function generateStyles(): string {
|
|
|
280
288
|
.ihooman-status-dot { width: 8px; height: 8px; border-radius: 50%; background: #22c55e; }
|
|
281
289
|
.ihooman-status-dot.offline { background: #f59e0b; }
|
|
282
290
|
.ihooman-header-actions { display: flex; gap: 8px; }
|
|
283
|
-
.ihooman-header-btn { width: 32px; height: 32px; border-radius: 8px; background: rgba(255,255,255,0.15); border: none; cursor: pointer; display: flex; align-items: center; justify-content: center; color: white; transition: background 0.2s; }
|
|
291
|
+
.ihooman-header-btn { width: 32px; height: 32px; border-radius: 8px !important; background: rgba(255,255,255,0.15); border: none; cursor: pointer; display: flex; align-items: center; justify-content: center; color: white; transition: background 0.2s; }
|
|
284
292
|
.ihooman-header-btn:hover { background: rgba(255,255,255,0.25); }
|
|
285
293
|
.ihooman-header-btn svg { width: 16px; height: 16px; }
|
|
286
294
|
.ihooman-messages { flex: 1; overflow-y: auto; padding: 16px; display: flex; flex-direction: column; gap: 12px; background: ${bgColor}; }
|
|
@@ -317,14 +325,14 @@ function generateStyles(): string {
|
|
|
317
325
|
.ihooman-powered { text-align: center; padding: 8px; font-size: 11px; color: ${mutedColor}; background: ${bgColor}; }
|
|
318
326
|
.ihooman-powered a { color: ${primaryColor}; text-decoration: none; }
|
|
319
327
|
.ihooman-error { padding: 16px; text-align: center; color: #ef4444; background: ${bgColor}; }
|
|
320
|
-
.ihooman-escalation-actions { display: flex; gap:
|
|
321
|
-
.ihooman-escalation-btn { display: inline-flex; align-items: center; gap:
|
|
322
|
-
.ihooman-escalation-btn svg { width: 16px; height: 16px; }
|
|
323
|
-
.ihooman-escalation-btn.primary { background: linear-gradient(135deg, ${gradientFrom}, ${gradientTo}); color: white; }
|
|
324
|
-
.ihooman-escalation-btn.primary:hover { transform: translateY(-1px); box-shadow: 0 4px 12px rgba(0, 174, 255, 0.3); }
|
|
325
|
-
.ihooman-escalation-btn.secondary { background: ${isDark ? 'rgba(255,255,255,0.1)' : 'rgba(0,0,0,0.05)'}; color: ${textColor}; border: 1px solid ${borderColor}; }
|
|
326
|
-
.ihooman-escalation-btn.secondary:hover { background: ${isDark ? 'rgba(255,255,255,0.15)' : 'rgba(0,0,0,0.08)'}; }
|
|
327
|
-
.ihooman-escalation-btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }
|
|
328
|
+
.ihooman-escalation-actions { display: flex; gap: 10px; margin-top: 12px; flex-wrap: wrap; }
|
|
329
|
+
.ihooman-widget .ihooman-escalation-btn { display: inline-flex !important; align-items: center !important; gap: 8px !important; padding: 10px 18px !important; border-radius: 8px !important; border: none !important; cursor: pointer !important; font-family: inherit !important; font-size: 13px !important; font-weight: 500 !important; transition: all 0.2s ease !important; width: auto !important; height: auto !important; min-width: 120px !important; }
|
|
330
|
+
.ihooman-widget .ihooman-escalation-btn svg { width: 16px !important; height: 16px !important; flex-shrink: 0 !important; }
|
|
331
|
+
.ihooman-widget .ihooman-escalation-btn.primary { background: linear-gradient(135deg, ${gradientFrom}, ${gradientTo}) !important; color: white !important; }
|
|
332
|
+
.ihooman-widget .ihooman-escalation-btn.primary:hover { transform: translateY(-1px); box-shadow: 0 4px 12px rgba(0, 174, 255, 0.3); }
|
|
333
|
+
.ihooman-widget .ihooman-escalation-btn.secondary { background: ${isDark ? 'rgba(255,255,255,0.1)' : 'rgba(0,0,0,0.05)'} !important; color: ${textColor} !important; border: 1px solid ${borderColor} !important; }
|
|
334
|
+
.ihooman-widget .ihooman-escalation-btn.secondary:hover { background: ${isDark ? 'rgba(255,255,255,0.15)' : 'rgba(0,0,0,0.08)'} !important; }
|
|
335
|
+
.ihooman-widget .ihooman-escalation-btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }
|
|
328
336
|
.ihooman-status-bar { padding: 10px 16px; text-align: center; font-size: 13px; display: none; }
|
|
329
337
|
.ihooman-status-bar.show { display: block; }
|
|
330
338
|
.ihooman-status-bar.waiting { background: #fef3c7; color: #92400e; }
|
|
@@ -344,6 +352,20 @@ function generateStyles(): string {
|
|
|
344
352
|
.ihooman-ticket-submit:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }
|
|
345
353
|
.ihooman-ticket-back { padding: 10px; background: transparent; color: ${mutedColor}; border: 1px solid ${borderColor}; border-radius: 10px; font-size: 13px; cursor: pointer; transition: all 0.2s; }
|
|
346
354
|
.ihooman-ticket-back:hover { background: ${isDark ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.03)'}; }
|
|
355
|
+
.ihooman-history-view { display: none; flex-direction: column; flex: 1; overflow: hidden; background: ${bgColor}; }
|
|
356
|
+
.ihooman-history-view.show { display: flex; }
|
|
357
|
+
.ihooman-history-header { padding: 12px 16px; border-bottom: 1px solid ${borderColor}; display: flex; justify-content: space-between; align-items: center; }
|
|
358
|
+
.ihooman-history-title { font-size: 14px; font-weight: 600; color: ${textColor}; margin: 0; }
|
|
359
|
+
.ihooman-history-new { display: inline-flex; align-items: center; gap: 4px; background: linear-gradient(135deg, ${gradientFrom}, ${gradientTo}); color: white; border: none; padding: 8px 16px; border-radius: 8px !important; font-size: 13px; font-weight: 500; cursor: pointer; transition: all 0.2s; }
|
|
360
|
+
.ihooman-history-new:hover { transform: translateY(-1px); box-shadow: 0 2px 8px rgba(0, 174, 255, 0.3); }
|
|
361
|
+
.ihooman-history-new svg { width: 14px; height: 14px; flex-shrink: 0; }
|
|
362
|
+
.ihooman-history-list { flex: 1; overflow-y: auto; padding: 8px; }
|
|
363
|
+
.ihooman-history-item { padding: 12px; border: 1px solid ${borderColor}; border-radius: 8px; margin-bottom: 6px; cursor: pointer; transition: all 0.2s; background: ${bgColor}; }
|
|
364
|
+
.ihooman-history-item:hover { background: ${isDark ? 'rgba(255,255,255,0.05)' : '#f8fafc'}; }
|
|
365
|
+
.ihooman-history-item.active { background: ${isDark ? 'rgba(0, 174, 255, 0.1)' : '#eff6ff'}; border-color: ${primaryColor}; }
|
|
366
|
+
.ihooman-history-preview { font-size: 13px; color: ${textColor}; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
367
|
+
.ihooman-history-meta { font-size: 11px; color: ${mutedColor}; margin-top: 4px; display: flex; justify-content: space-between; }
|
|
368
|
+
.ihooman-history-empty { padding: 40px; text-align: center; color: ${mutedColor}; font-size: 14px; }
|
|
347
369
|
@media (max-width: 480px) { .ihooman-window { width: calc(100vw - 20px); height: calc(100vh - 100px); left: 10px; right: 10px; bottom: 80px; max-height: none; } .ihooman-toggle { ${positionRight ? 'right: 16px' : 'left: 16px'}; bottom: 16px; } }
|
|
348
370
|
`;
|
|
349
371
|
}
|
|
@@ -382,7 +404,8 @@ function createWidget(): void {
|
|
|
382
404
|
<div class="ihooman-header-status"><span class="ihooman-status-dot"></span><span class="ihooman-status-text">Online</span></div>
|
|
383
405
|
</div>
|
|
384
406
|
<div class="ihooman-header-actions">
|
|
385
|
-
<button class="ihooman-header-btn" data-action="
|
|
407
|
+
<button class="ihooman-header-btn" data-action="history" title="Chat history">${icons.history}</button>
|
|
408
|
+
<button class="ihooman-header-btn" data-action="refresh" title="New conversation">${icons.plus}</button>
|
|
386
409
|
<button class="ihooman-header-btn" data-action="minimize" title="Minimize">${icons.minimize}</button>
|
|
387
410
|
</div>
|
|
388
411
|
</div>
|
|
@@ -411,6 +434,15 @@ function createWidget(): void {
|
|
|
411
434
|
<button class="ihooman-ticket-back" id="ihooman-ticket-back">← Back to Chat</button>
|
|
412
435
|
</div>
|
|
413
436
|
|
|
437
|
+
<!-- History View -->
|
|
438
|
+
<div class="ihooman-history-view">
|
|
439
|
+
<div class="ihooman-history-header">
|
|
440
|
+
<span class="ihooman-history-title">Your Conversations</span>
|
|
441
|
+
<button class="ihooman-history-new">${icons.plus} New Chat</button>
|
|
442
|
+
</div>
|
|
443
|
+
<div class="ihooman-history-list"></div>
|
|
444
|
+
</div>
|
|
445
|
+
|
|
414
446
|
${config.poweredBy ? `<div class="ihooman-powered">Powered by <a href="https://ihooman.ai" target="_blank" rel="noopener">Ihooman AI</a></div>` : ''}
|
|
415
447
|
</div>
|
|
416
448
|
</div>
|
|
@@ -426,6 +458,9 @@ function createWidget(): void {
|
|
|
426
458
|
window: widget.querySelector('.ihooman-window') as HTMLElement,
|
|
427
459
|
chatView: widget.querySelector('.ihooman-chat-view') as HTMLElement,
|
|
428
460
|
ticketView: widget.querySelector('.ihooman-ticket-view') as HTMLElement,
|
|
461
|
+
historyView: widget.querySelector('.ihooman-history-view') as HTMLElement,
|
|
462
|
+
historyList: widget.querySelector('.ihooman-history-list') as HTMLElement,
|
|
463
|
+
historyNewBtn: widget.querySelector('.ihooman-history-new') as HTMLButtonElement,
|
|
429
464
|
messages: widget.querySelector('.ihooman-messages') as HTMLElement,
|
|
430
465
|
input: widget.querySelector('.ihooman-input') as HTMLTextAreaElement,
|
|
431
466
|
sendBtn: widget.querySelector('.ihooman-input-btn.send') as HTMLButtonElement,
|
|
@@ -478,6 +513,7 @@ function setupEventListeners(): void {
|
|
|
478
513
|
|
|
479
514
|
elements.widget?.querySelector('[data-action="refresh"]')?.addEventListener('click', startNewConversation);
|
|
480
515
|
elements.widget?.querySelector('[data-action="minimize"]')?.addEventListener('click', close);
|
|
516
|
+
elements.widget?.querySelector('[data-action="history"]')?.addEventListener('click', toggleHistoryView);
|
|
481
517
|
|
|
482
518
|
// Ticket form buttons
|
|
483
519
|
if (elements.ticketSubmitBtn) {
|
|
@@ -486,6 +522,14 @@ function setupEventListeners(): void {
|
|
|
486
522
|
if (elements.ticketBackBtn) {
|
|
487
523
|
elements.ticketBackBtn.addEventListener('click', () => showView('chat'));
|
|
488
524
|
}
|
|
525
|
+
|
|
526
|
+
// History view buttons
|
|
527
|
+
if (elements.historyNewBtn) {
|
|
528
|
+
elements.historyNewBtn.addEventListener('click', () => {
|
|
529
|
+
startNewConversation();
|
|
530
|
+
showView('chat');
|
|
531
|
+
});
|
|
532
|
+
}
|
|
489
533
|
}
|
|
490
534
|
|
|
491
535
|
|
|
@@ -585,9 +629,9 @@ function handleEscalationAction(action: 'live-agent' | 'create-ticket'): void {
|
|
|
585
629
|
}
|
|
586
630
|
|
|
587
631
|
/**
|
|
588
|
-
* Switch between chat and
|
|
632
|
+
* Switch between chat, ticket, and history views
|
|
589
633
|
*/
|
|
590
|
-
function showView(view: 'chat' | 'ticket'): void {
|
|
634
|
+
function showView(view: 'chat' | 'ticket' | 'history'): void {
|
|
591
635
|
currentView = view;
|
|
592
636
|
|
|
593
637
|
if (elements.chatView) {
|
|
@@ -596,6 +640,177 @@ function showView(view: 'chat' | 'ticket'): void {
|
|
|
596
640
|
if (elements.ticketView) {
|
|
597
641
|
elements.ticketView.classList.toggle('show', view === 'ticket');
|
|
598
642
|
}
|
|
643
|
+
if (elements.historyView) {
|
|
644
|
+
elements.historyView.classList.toggle('show', view === 'history');
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// Load history when showing history view
|
|
648
|
+
if (view === 'history') {
|
|
649
|
+
loadConversationHistory();
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* Toggle between chat and history views
|
|
655
|
+
*/
|
|
656
|
+
function toggleHistoryView(): void {
|
|
657
|
+
if (currentView === 'history') {
|
|
658
|
+
showView('chat');
|
|
659
|
+
} else {
|
|
660
|
+
showView('history');
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* Format time ago string
|
|
666
|
+
*/
|
|
667
|
+
function timeAgo(date: Date | string): string {
|
|
668
|
+
const diff = Date.now() - new Date(date).getTime();
|
|
669
|
+
if (diff < 60000) return 'now';
|
|
670
|
+
if (diff < 3600000) return Math.floor(diff / 60000) + 'm';
|
|
671
|
+
if (diff < 86400000) return Math.floor(diff / 3600000) + 'h';
|
|
672
|
+
return Math.floor(diff / 86400000) + 'd';
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* Conversation history item interface
|
|
677
|
+
*/
|
|
678
|
+
interface ConversationHistoryItem {
|
|
679
|
+
session_id: string;
|
|
680
|
+
preview: string;
|
|
681
|
+
message_count: number;
|
|
682
|
+
status: string;
|
|
683
|
+
updated_at: string;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* Load conversation history from the server
|
|
688
|
+
*/
|
|
689
|
+
async function loadConversationHistory(): Promise<void> {
|
|
690
|
+
if (!elements.historyList || !state.visitorId) return;
|
|
691
|
+
|
|
692
|
+
elements.historyList.innerHTML = '<div class="ihooman-history-empty">Loading...</div>';
|
|
693
|
+
|
|
694
|
+
try {
|
|
695
|
+
const response = await fetch(
|
|
696
|
+
`${config.serverUrl}/api/widget/conversations?widget_id=${config.widgetId}&visitor_id=${state.visitorId}&limit=20`
|
|
697
|
+
);
|
|
698
|
+
|
|
699
|
+
if (!response.ok) {
|
|
700
|
+
throw new Error('Failed to load history');
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
const conversations: ConversationHistoryItem[] = await response.json();
|
|
704
|
+
|
|
705
|
+
if (!conversations.length) {
|
|
706
|
+
elements.historyList.innerHTML = '<div class="ihooman-history-empty">No conversations yet</div>';
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
elements.historyList.innerHTML = conversations.map(conv => `
|
|
711
|
+
<div class="ihooman-history-item ${conv.session_id === state.sessionId ? 'active' : ''}" data-session-id="${conv.session_id}">
|
|
712
|
+
<div class="ihooman-history-preview">${escapeHtml(conv.preview || 'New conversation')}</div>
|
|
713
|
+
<div class="ihooman-history-meta">
|
|
714
|
+
<span>${conv.message_count} msgs</span>
|
|
715
|
+
<span>${conv.status === 'pending' ? '🟡 Pending' : conv.status === 'closed' ? '✓ Closed' : ''} ${timeAgo(conv.updated_at)}</span>
|
|
716
|
+
</div>
|
|
717
|
+
</div>
|
|
718
|
+
`).join('');
|
|
719
|
+
|
|
720
|
+
// Add click handlers to history items
|
|
721
|
+
elements.historyList.querySelectorAll('.ihooman-history-item').forEach(item => {
|
|
722
|
+
item.addEventListener('click', () => {
|
|
723
|
+
const sessionId = (item as HTMLElement).dataset.sessionId;
|
|
724
|
+
if (sessionId) {
|
|
725
|
+
switchToConversation(sessionId);
|
|
726
|
+
}
|
|
727
|
+
});
|
|
728
|
+
});
|
|
729
|
+
|
|
730
|
+
} catch (error) {
|
|
731
|
+
console.error('Error loading conversation history:', error);
|
|
732
|
+
elements.historyList.innerHTML = '<div class="ihooman-history-empty">Failed to load history</div>';
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
/**
|
|
737
|
+
* Switch to a specific conversation
|
|
738
|
+
*/
|
|
739
|
+
async function switchToConversation(sessionId: string): Promise<void> {
|
|
740
|
+
state.sessionId = sessionId;
|
|
741
|
+
if (config.persistSession) {
|
|
742
|
+
storage('session_id', sessionId);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// Clear current messages
|
|
746
|
+
if (elements.messages) {
|
|
747
|
+
elements.messages.innerHTML = '';
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
// Reset live agent mode
|
|
751
|
+
isLiveAgentMode = false;
|
|
752
|
+
stopLiveAgentPolling();
|
|
753
|
+
updateStatusBar('hidden');
|
|
754
|
+
|
|
755
|
+
// Reconnect WebSocket with new session
|
|
756
|
+
intentionalDisconnect = true;
|
|
757
|
+
if (ws) {
|
|
758
|
+
ws.close();
|
|
759
|
+
ws = null;
|
|
760
|
+
}
|
|
761
|
+
connectWebSocket();
|
|
762
|
+
|
|
763
|
+
// Switch to chat view
|
|
764
|
+
showView('chat');
|
|
765
|
+
|
|
766
|
+
// Load conversation messages
|
|
767
|
+
await loadConversationMessages(sessionId);
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
/**
|
|
771
|
+
* Load messages for a specific conversation
|
|
772
|
+
*/
|
|
773
|
+
async function loadConversationMessages(sessionId: string): Promise<void> {
|
|
774
|
+
try {
|
|
775
|
+
const response = await fetch(
|
|
776
|
+
`${config.serverUrl}/api/widget/transcript/${sessionId}?widget_id=${config.widgetId}`
|
|
777
|
+
);
|
|
778
|
+
|
|
779
|
+
if (!response.ok) {
|
|
780
|
+
throw new Error('Failed to load messages');
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
const data = await response.json();
|
|
784
|
+
|
|
785
|
+
if (elements.messages) {
|
|
786
|
+
elements.messages.innerHTML = '';
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
// Add messages to the chat
|
|
790
|
+
if (data.messages && data.messages.length > 0) {
|
|
791
|
+
data.messages.forEach((msg: { content: string; sender_type: string; extra_data?: Record<string, unknown> }) => {
|
|
792
|
+
const sender = msg.sender_type === 'user' ? 'user' : 'bot';
|
|
793
|
+
addMessage(msg.content, sender, msg.extra_data || {});
|
|
794
|
+
});
|
|
795
|
+
} else if (config.welcomeMessage) {
|
|
796
|
+
addMessage(config.welcomeMessage, 'bot');
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// Check conversation status
|
|
800
|
+
if (data.status === 'pending') {
|
|
801
|
+
isLiveAgentMode = true;
|
|
802
|
+
startLiveAgentPolling();
|
|
803
|
+
updateStatusBar('waiting', '⏳ Waiting for agent...');
|
|
804
|
+
} else if (data.status === 'closed') {
|
|
805
|
+
updateStatusBar('hidden');
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
} catch (error) {
|
|
809
|
+
console.error('Error loading conversation messages:', error);
|
|
810
|
+
if (config.welcomeMessage) {
|
|
811
|
+
addMessage(config.welcomeMessage, 'bot');
|
|
812
|
+
}
|
|
813
|
+
}
|
|
599
814
|
}
|
|
600
815
|
|
|
601
816
|
/**
|
|
@@ -991,6 +1206,12 @@ function connectWebSocket(chatEndpoint?: string): void {
|
|
|
991
1206
|
updateStatus(false);
|
|
992
1207
|
emit('disconnected');
|
|
993
1208
|
|
|
1209
|
+
// Don't reconnect if this was an intentional disconnect (e.g., starting new conversation)
|
|
1210
|
+
if (intentionalDisconnect) {
|
|
1211
|
+
intentionalDisconnect = false;
|
|
1212
|
+
return;
|
|
1213
|
+
}
|
|
1214
|
+
|
|
994
1215
|
// Attempt reconnection with exponential backoff
|
|
995
1216
|
if (reconnectAttempts < maxReconnectAttempts) {
|
|
996
1217
|
reconnectAttempts++;
|
|
@@ -1204,11 +1425,17 @@ function startNewConversation(): void {
|
|
|
1204
1425
|
// Stop any live agent polling
|
|
1205
1426
|
stopLiveAgentPolling();
|
|
1206
1427
|
|
|
1207
|
-
//
|
|
1428
|
+
// Reset reconnect attempts for fresh connection
|
|
1429
|
+
reconnectAttempts = 0;
|
|
1430
|
+
|
|
1431
|
+
// Close existing WebSocket with intentional flag to prevent auto-reconnect
|
|
1208
1432
|
if (ws) {
|
|
1433
|
+
intentionalDisconnect = true;
|
|
1209
1434
|
ws.close();
|
|
1210
1435
|
ws = null;
|
|
1211
1436
|
}
|
|
1437
|
+
|
|
1438
|
+
// Connect with new visitor ID
|
|
1212
1439
|
connectWebSocket();
|
|
1213
1440
|
|
|
1214
1441
|
// Show welcome message
|
|
@@ -1229,7 +1456,8 @@ function destroy(): void {
|
|
|
1229
1456
|
// Stop live agent polling
|
|
1230
1457
|
stopLiveAgentPolling();
|
|
1231
1458
|
|
|
1232
|
-
// Close WebSocket
|
|
1459
|
+
// Close WebSocket (intentionally, don't reconnect)
|
|
1460
|
+
intentionalDisconnect = true;
|
|
1233
1461
|
if (ws) {
|
|
1234
1462
|
ws.close();
|
|
1235
1463
|
ws = null;
|
|
@@ -1262,6 +1490,7 @@ function destroy(): void {
|
|
|
1262
1490
|
};
|
|
1263
1491
|
elements = {};
|
|
1264
1492
|
reconnectAttempts = 0;
|
|
1493
|
+
intentionalDisconnect = false;
|
|
1265
1494
|
}
|
|
1266
1495
|
|
|
1267
1496
|
/**
|