@product7/feedback-sdk 1.2.4 → 1.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +176 -7
- package/dist/README.md +176 -7
- package/dist/feedback-sdk.js +8156 -5140
- package/dist/feedback-sdk.js.map +1 -1
- package/dist/feedback-sdk.min.js +1 -1
- package/dist/feedback-sdk.min.js.map +1 -1
- package/package.json +1 -1
- package/src/core/APIService.js +643 -0
- package/src/core/FeedbackSDK.js +115 -5
- package/src/core/WebSocketService.js +273 -0
- package/src/index.js +3 -0
- package/src/styles/messengerStyles.js +100 -0
- package/src/styles/styles.js +747 -1
- package/src/widgets/BaseWidget.js +96 -0
- package/src/widgets/ButtonWidget.js +93 -91
- package/src/widgets/ChangelogWidget.js +619 -0
- package/src/widgets/MessengerWidget.js +374 -89
- package/src/widgets/WidgetFactory.js +2 -0
- package/src/widgets/messenger/MessengerState.js +12 -0
- package/src/widgets/messenger/components/NavigationTabs.js +1 -1
- package/src/widgets/messenger/views/ChatView.js +121 -16
- package/src/widgets/messenger/views/ConversationsView.js +12 -11
- package/src/widgets/messenger/views/HomeView.js +23 -1
- package/types/index.d.ts +152 -123
package/package.json
CHANGED
package/src/core/APIService.js
CHANGED
|
@@ -9,6 +9,175 @@ const MOCK_CONFIG = {
|
|
|
9
9
|
displayMode: 'modal',
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
+
// Mock changelogs for development
|
|
13
|
+
const MOCK_CHANGELOGS = [
|
|
14
|
+
{
|
|
15
|
+
id: 'changelog_1',
|
|
16
|
+
title:
|
|
17
|
+
'Feature prioritization, multiple roadmaps, and feature request analytics.',
|
|
18
|
+
excerpt:
|
|
19
|
+
'We are super excited to bring you the long-waited feature prioritization together with many quality updates!',
|
|
20
|
+
description:
|
|
21
|
+
'We are super excited to bring you the long-waited feature prioritization together with many quality updates!',
|
|
22
|
+
cover_image:
|
|
23
|
+
'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAwIiBoZWlnaHQ9IjI1MCIgdmlld0JveD0iMCAwIDQwMCAyNTAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHJlY3Qgd2lkdGg9IjQwMCIgaGVpZ2h0PSIyNTAiIGZpbGw9IiNEQkVBRkUiLz48cmVjdCB4PSIyMCIgeT0iMjAiIHdpZHRoPSIzNjAiIGhlaWdodD0iMjEwIiByeD0iOCIgZmlsbD0id2hpdGUiIHN0cm9rZT0iIzE1NUVFRiIgc3Ryb2tlLXdpZHRoPSIyIi8+PHJlY3QgeD0iMzAiIHk9IjQwIiB3aWR0aD0iODAiIGhlaWdodD0iOCIgcng9IjQiIGZpbGw9IiNFNUU3RUIiLz48cmVjdCB4PSIzMCIgeT0iNjAiIHdpZHRoPSIxNDAiIGhlaWdodD0iNDAiIHJ4PSI0IiBmaWxsPSIjRjNGNEY2Ii8+PHRleHQgeD0iNDAiIHk9Ijg1IiBmb250LWZhbWlseT0ic3lzdGVtLXVpIiBmb250LXNpemU9IjE2IiBmb250LXdlaWdodD0iNjAwIiBmaWxsPSIjMUYyOTM3Ij4yMDA8L3RleHQ+PHRleHQgeD0iNDAiIHk9Ijk1IiBmb250LWZhbWlseT0ic3lzdGVtLXVpIiBmb250LXNpemU9IjgiIGZpbGw9IiM2QjcyODAiPlBvc3RzPC90ZXh0PjxyZWN0IHg9IjE4MCIgeT0iNjAiIHdpZHRoPSI4MCIgaGVpZ2h0PSI0MCIgcng9IjQiIGZpbGw9IiNGM0Y0RjYiLz48cmVjdCB4PSIyNzAiIHk9IjYwIiB3aWR0aD0iODAiIGhlaWdodD0iNDAiIHJ4PSI0IiBmaWxsPSIjRjNGNEY2Ii8+PHBhdGggZD0iTTMwIDEzMEgzNzBNMzAgMTUwSDM3ME0zMCAxNzBIMzcwTTMwIDE5MEgzNzAiIHN0cm9rZT0iI0U1RTdFQiIgc3Ryb2tlLXdpZHRoPSIxIi8+PHBhdGggZD0iTTUwIDE2MEwxMDAgMTQwTDE1MCAxNTBMMjAwIDEzMEwyNTAgMTM1TDMwMCAxMjBMMzUwIDEyNSIgc3Ryb2tlPSIjMTU1RUVGIiBzdHJva2Utd2lkdGg9IjIiIGZpbGw9Im5vbmUiLz48L3N2Zz4=',
|
|
24
|
+
slug: 'feature-prioritization-roadmaps-analytics',
|
|
25
|
+
published_at: '2025-03-15T10:00:00Z',
|
|
26
|
+
labels: [
|
|
27
|
+
{ name: 'New Feature', color: '#10B981' },
|
|
28
|
+
{ name: 'Analytics', color: '#6366F1' },
|
|
29
|
+
],
|
|
30
|
+
status: 'published',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: 'changelog_2',
|
|
34
|
+
title: 'Dark mode support and UI improvements',
|
|
35
|
+
excerpt:
|
|
36
|
+
'You asked for it, we delivered! Dark mode is now available across all pages.',
|
|
37
|
+
description:
|
|
38
|
+
'You asked for it, we delivered! Dark mode is now available across all pages.',
|
|
39
|
+
cover_image:
|
|
40
|
+
'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAwIiBoZWlnaHQ9IjI1MCIgdmlld0JveD0iMCAwIDQwMCAyNTAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHJlY3Qgd2lkdGg9IjQwMCIgaGVpZ2h0PSIyNTAiIGZpbGw9IiMxRjI5MzciLz48cmVjdCB4PSIyMCIgeT0iMjAiIHdpZHRoPSIzNjAiIGhlaWdodD0iMjEwIiByeD0iOCIgZmlsbD0iIzM3NDE1MSIgc3Ryb2tlPSIjNEI1NTYzIiBzdHJva2Utd2lkdGg9IjEiLz48Y2lyY2xlIGN4PSIyMDAiIGN5PSIxMjUiIHI9IjQwIiBmaWxsPSIjRkJCRjI0Ii8+PHBhdGggZD0iTTIyMCAxMjVDMjIwIDEzNiAyMTEgMTQ1IDIwMCAxNDVDMTg5IDE0NSAxODAgMTM2IDE4MCAxMjVDMTgwIDExNCAxODkgMTA1IDIwMCAxMDVDMjExIDEwNSAyMjAgMTE0IDIyMCAxMjVaIiBmaWxsPSIjMUYyOTM3Ii8+PC9zdmc+',
|
|
41
|
+
slug: 'dark-mode-ui-improvements',
|
|
42
|
+
published_at: '2025-03-01T10:00:00Z',
|
|
43
|
+
labels: [{ name: 'Enhancement', color: '#8B5CF6' }],
|
|
44
|
+
status: 'published',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: 'changelog_3',
|
|
48
|
+
title: 'Performance improvements and bug fixes',
|
|
49
|
+
excerpt:
|
|
50
|
+
'This release includes major performance improvements and several bug fixes.',
|
|
51
|
+
description:
|
|
52
|
+
'This release includes major performance improvements and several bug fixes.',
|
|
53
|
+
slug: 'performance-improvements-bug-fixes',
|
|
54
|
+
published_at: '2025-02-15T10:00:00Z',
|
|
55
|
+
labels: [
|
|
56
|
+
{ name: 'Bug Fix', color: '#EF4444' },
|
|
57
|
+
{ name: 'Performance', color: '#F59E0B' },
|
|
58
|
+
],
|
|
59
|
+
status: 'published',
|
|
60
|
+
},
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
// Mock conversations for development
|
|
64
|
+
const MOCK_CONVERSATIONS = [
|
|
65
|
+
{
|
|
66
|
+
id: 'conv_1',
|
|
67
|
+
subject: 'Question about pricing',
|
|
68
|
+
status: 'open',
|
|
69
|
+
last_message_at: new Date(Date.now() - 49 * 60 * 1000).toISOString(),
|
|
70
|
+
created_at: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString(),
|
|
71
|
+
unread: 1,
|
|
72
|
+
assigned_user: {
|
|
73
|
+
id: 'user_1',
|
|
74
|
+
name: 'Sarah',
|
|
75
|
+
avatar: null,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
id: 'conv_2',
|
|
80
|
+
subject: 'Feature request',
|
|
81
|
+
status: 'open',
|
|
82
|
+
last_message_at: new Date(Date.now() - 6 * 24 * 60 * 60 * 1000).toISOString(),
|
|
83
|
+
created_at: new Date(Date.now() - 10 * 24 * 60 * 60 * 1000).toISOString(),
|
|
84
|
+
unread: 0,
|
|
85
|
+
assigned_user: {
|
|
86
|
+
id: 'user_2',
|
|
87
|
+
name: 'Tom',
|
|
88
|
+
avatar: null,
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
// Mock messages for development
|
|
94
|
+
const MOCK_MESSAGES = {
|
|
95
|
+
conv_1: [
|
|
96
|
+
{
|
|
97
|
+
id: 'msg_1',
|
|
98
|
+
content: "Hi there! 👋 I'm Sarah. How can I help you today?",
|
|
99
|
+
sender_type: 'agent',
|
|
100
|
+
sender_name: 'Sarah',
|
|
101
|
+
sender_avatar: null,
|
|
102
|
+
created_at: new Date(Date.now() - 50 * 60 * 1000).toISOString(),
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
id: 'msg_2',
|
|
106
|
+
content: 'Hi! I have a question about your enterprise pricing.',
|
|
107
|
+
sender_type: 'customer',
|
|
108
|
+
created_at: new Date(Date.now() - 49 * 60 * 1000).toISOString(),
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
conv_2: [
|
|
112
|
+
{
|
|
113
|
+
id: 'msg_3',
|
|
114
|
+
content: "Hello! I'm Tom from the product team.",
|
|
115
|
+
sender_type: 'agent',
|
|
116
|
+
sender_name: 'Tom',
|
|
117
|
+
sender_avatar: null,
|
|
118
|
+
created_at: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(),
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
id: 'msg_4',
|
|
122
|
+
content: 'I would love to see a dark mode feature!',
|
|
123
|
+
sender_type: 'customer',
|
|
124
|
+
created_at: new Date(Date.now() - 6 * 24 * 60 * 60 * 1000 - 30 * 60 * 1000).toISOString(),
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
id: 'msg_5',
|
|
128
|
+
content: "Great suggestion! That feature will be available next week. I'll let you know when it's ready.",
|
|
129
|
+
sender_type: 'agent',
|
|
130
|
+
sender_name: 'Tom',
|
|
131
|
+
sender_avatar: null,
|
|
132
|
+
created_at: new Date(Date.now() - 6 * 24 * 60 * 60 * 1000).toISOString(),
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// Mock help collections for development
|
|
138
|
+
const MOCK_HELP_COLLECTIONS = [
|
|
139
|
+
{
|
|
140
|
+
id: 'collection_1',
|
|
141
|
+
title: 'Product Overview',
|
|
142
|
+
description: 'See how your AI-first customer service solution works.',
|
|
143
|
+
articleCount: 24,
|
|
144
|
+
icon: 'ph-book-open',
|
|
145
|
+
url: '#',
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
id: 'collection_2',
|
|
149
|
+
title: 'Getting Started',
|
|
150
|
+
description: 'Everything you need to know to get started with Product7.',
|
|
151
|
+
articleCount: 30,
|
|
152
|
+
icon: 'ph-rocket',
|
|
153
|
+
url: '#',
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
id: 'collection_3',
|
|
157
|
+
title: 'AI Agent',
|
|
158
|
+
description: 'Resolving customer questions instantly and accurately—from live chat to email.',
|
|
159
|
+
articleCount: 82,
|
|
160
|
+
icon: 'ph-robot',
|
|
161
|
+
url: '#',
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
id: 'collection_4',
|
|
165
|
+
title: 'Channels',
|
|
166
|
+
description: 'Enabling the channels you use to communicate with customers, all from the Inbox.',
|
|
167
|
+
articleCount: 45,
|
|
168
|
+
icon: 'ph-chat-circle',
|
|
169
|
+
url: '#',
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
id: 'collection_5',
|
|
173
|
+
title: 'Billing & Payments',
|
|
174
|
+
description: 'Manage your subscription, invoices, and payment methods.',
|
|
175
|
+
articleCount: 12,
|
|
176
|
+
icon: 'ph-credit-card',
|
|
177
|
+
url: '#',
|
|
178
|
+
},
|
|
179
|
+
];
|
|
180
|
+
|
|
12
181
|
// Mock surveys for development
|
|
13
182
|
const MOCK_SURVEYS = [
|
|
14
183
|
{
|
|
@@ -430,6 +599,480 @@ export class APIService {
|
|
|
430
599
|
}
|
|
431
600
|
}
|
|
432
601
|
|
|
602
|
+
// ==========================================
|
|
603
|
+
// MESSENGER / CHAT ENDPOINTS
|
|
604
|
+
// ==========================================
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Get messenger settings
|
|
608
|
+
* @returns {Promise<Object>} Messenger settings
|
|
609
|
+
*/
|
|
610
|
+
async getMessengerSettings() {
|
|
611
|
+
if (!this.isSessionValid()) {
|
|
612
|
+
await this.init();
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
if (this.mock) {
|
|
616
|
+
return {
|
|
617
|
+
status: true,
|
|
618
|
+
data: {
|
|
619
|
+
enabled: true,
|
|
620
|
+
greeting_message: 'Hi there! How can we help you today?',
|
|
621
|
+
team_name: 'Support Team',
|
|
622
|
+
response_time: 'Usually replies within a few minutes',
|
|
623
|
+
},
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
return this._makeRequest('/widget/messenger/settings', {
|
|
628
|
+
method: 'GET',
|
|
629
|
+
headers: { Authorization: `Bearer ${this.sessionToken}` },
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Check if agents are online
|
|
635
|
+
* @returns {Promise<Object>} Agent availability status
|
|
636
|
+
*/
|
|
637
|
+
async checkAgentsOnline() {
|
|
638
|
+
if (!this.isSessionValid()) {
|
|
639
|
+
await this.init();
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
if (this.mock) {
|
|
643
|
+
return {
|
|
644
|
+
status: true,
|
|
645
|
+
data: {
|
|
646
|
+
agents_online: true,
|
|
647
|
+
online_count: 2,
|
|
648
|
+
response_time: 'Usually replies within a few minutes',
|
|
649
|
+
},
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
return this._makeRequest('/widget/messenger/agents/online', {
|
|
654
|
+
method: 'GET',
|
|
655
|
+
headers: { Authorization: `Bearer ${this.sessionToken}` },
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Get all conversations for the current contact
|
|
661
|
+
* @param {Object} options - Query options
|
|
662
|
+
* @param {number} options.page - Page number
|
|
663
|
+
* @param {number} options.limit - Items per page
|
|
664
|
+
* @returns {Promise<Object>} Conversations list
|
|
665
|
+
*/
|
|
666
|
+
async getConversations(options = {}) {
|
|
667
|
+
if (!this.isSessionValid()) {
|
|
668
|
+
await this.init();
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
if (this.mock) {
|
|
672
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
673
|
+
return {
|
|
674
|
+
status: true,
|
|
675
|
+
data: MOCK_CONVERSATIONS,
|
|
676
|
+
meta: { total: MOCK_CONVERSATIONS.length, page: 1, limit: 20 },
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
const params = new URLSearchParams();
|
|
681
|
+
if (options.page) params.append('page', options.page);
|
|
682
|
+
if (options.limit) params.append('limit', options.limit);
|
|
683
|
+
|
|
684
|
+
const endpoint = `/widget/messenger/conversations${params.toString() ? '?' + params.toString() : ''}`;
|
|
685
|
+
return this._makeRequest(endpoint, {
|
|
686
|
+
method: 'GET',
|
|
687
|
+
headers: { Authorization: `Bearer ${this.sessionToken}` },
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/**
|
|
692
|
+
* Get a single conversation with messages
|
|
693
|
+
* @param {string} conversationId - Conversation ID
|
|
694
|
+
* @returns {Promise<Object>} Conversation with messages
|
|
695
|
+
*/
|
|
696
|
+
async getConversation(conversationId) {
|
|
697
|
+
if (!this.isSessionValid()) {
|
|
698
|
+
await this.init();
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
if (this.mock) {
|
|
702
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
703
|
+
const conv = MOCK_CONVERSATIONS.find((c) => c.id === conversationId);
|
|
704
|
+
return {
|
|
705
|
+
status: true,
|
|
706
|
+
data: {
|
|
707
|
+
...conv,
|
|
708
|
+
messages: MOCK_MESSAGES[conversationId] || [],
|
|
709
|
+
},
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
return this._makeRequest(`/widget/messenger/conversations/${conversationId}`, {
|
|
714
|
+
method: 'GET',
|
|
715
|
+
headers: { Authorization: `Bearer ${this.sessionToken}` },
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
/**
|
|
720
|
+
* Get messages for a conversation
|
|
721
|
+
* @param {string} conversationId - Conversation ID
|
|
722
|
+
* @param {Object} options - Query options
|
|
723
|
+
* @returns {Promise<Object>} Messages list
|
|
724
|
+
*/
|
|
725
|
+
async getMessages(conversationId, options = {}) {
|
|
726
|
+
if (!this.isSessionValid()) {
|
|
727
|
+
await this.init();
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
if (this.mock) {
|
|
731
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
732
|
+
return {
|
|
733
|
+
status: true,
|
|
734
|
+
data: MOCK_MESSAGES[conversationId] || [],
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
const params = new URLSearchParams();
|
|
739
|
+
if (options.page) params.append('page', options.page);
|
|
740
|
+
if (options.limit) params.append('limit', options.limit);
|
|
741
|
+
|
|
742
|
+
const endpoint = `/widget/messenger/conversations/${conversationId}/messages${params.toString() ? '?' + params.toString() : ''}`;
|
|
743
|
+
return this._makeRequest(endpoint, {
|
|
744
|
+
method: 'GET',
|
|
745
|
+
headers: { Authorization: `Bearer ${this.sessionToken}` },
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* Start a new conversation
|
|
751
|
+
* @param {Object} data - Conversation data
|
|
752
|
+
* @param {string} data.message - Initial message content
|
|
753
|
+
* @param {string} data.subject - Optional subject
|
|
754
|
+
* @returns {Promise<Object>} Created conversation
|
|
755
|
+
*/
|
|
756
|
+
async startConversation(data) {
|
|
757
|
+
if (!this.isSessionValid()) {
|
|
758
|
+
await this.init();
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
if (this.mock) {
|
|
762
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
763
|
+
const newConv = {
|
|
764
|
+
id: 'conv_' + Date.now(),
|
|
765
|
+
subject: data.subject || 'New conversation',
|
|
766
|
+
status: 'open',
|
|
767
|
+
last_message_at: new Date().toISOString(),
|
|
768
|
+
created_at: new Date().toISOString(),
|
|
769
|
+
messages: [
|
|
770
|
+
{
|
|
771
|
+
id: 'msg_' + Date.now(),
|
|
772
|
+
content: data.message,
|
|
773
|
+
sender_type: 'customer',
|
|
774
|
+
created_at: new Date().toISOString(),
|
|
775
|
+
},
|
|
776
|
+
],
|
|
777
|
+
};
|
|
778
|
+
MOCK_CONVERSATIONS.unshift(newConv);
|
|
779
|
+
MOCK_MESSAGES[newConv.id] = newConv.messages;
|
|
780
|
+
return { status: true, data: newConv };
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
return this._makeRequest('/widget/messenger/conversations', {
|
|
784
|
+
method: 'POST',
|
|
785
|
+
headers: {
|
|
786
|
+
'Content-Type': 'application/json',
|
|
787
|
+
Authorization: `Bearer ${this.sessionToken}`,
|
|
788
|
+
},
|
|
789
|
+
body: JSON.stringify({
|
|
790
|
+
message: data.message,
|
|
791
|
+
subject: data.subject || '',
|
|
792
|
+
}),
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/**
|
|
797
|
+
* Send a message in a conversation
|
|
798
|
+
* @param {string} conversationId - Conversation ID
|
|
799
|
+
* @param {Object} data - Message data
|
|
800
|
+
* @param {string} data.content - Message content
|
|
801
|
+
* @returns {Promise<Object>} Sent message
|
|
802
|
+
*/
|
|
803
|
+
async sendMessage(conversationId, data) {
|
|
804
|
+
if (!this.isSessionValid()) {
|
|
805
|
+
await this.init();
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
if (this.mock) {
|
|
809
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
810
|
+
const newMessage = {
|
|
811
|
+
id: 'msg_' + Date.now(),
|
|
812
|
+
content: data.content,
|
|
813
|
+
sender_type: 'customer',
|
|
814
|
+
created_at: new Date().toISOString(),
|
|
815
|
+
};
|
|
816
|
+
if (!MOCK_MESSAGES[conversationId]) {
|
|
817
|
+
MOCK_MESSAGES[conversationId] = [];
|
|
818
|
+
}
|
|
819
|
+
MOCK_MESSAGES[conversationId].push(newMessage);
|
|
820
|
+
return { status: true, data: newMessage };
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
return this._makeRequest(`/widget/messenger/conversations/${conversationId}/messages`, {
|
|
824
|
+
method: 'POST',
|
|
825
|
+
headers: {
|
|
826
|
+
'Content-Type': 'application/json',
|
|
827
|
+
Authorization: `Bearer ${this.sessionToken}`,
|
|
828
|
+
},
|
|
829
|
+
body: JSON.stringify({ content: data.content }),
|
|
830
|
+
});
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
/**
|
|
834
|
+
* Send typing indicator
|
|
835
|
+
* @param {string} conversationId - Conversation ID
|
|
836
|
+
* @param {boolean} isTyping - Whether user is typing
|
|
837
|
+
* @returns {Promise<Object>} Response
|
|
838
|
+
*/
|
|
839
|
+
async sendTypingIndicator(conversationId, isTyping) {
|
|
840
|
+
if (!this.isSessionValid()) {
|
|
841
|
+
await this.init();
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
if (this.mock) {
|
|
845
|
+
return { status: true };
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
return this._makeRequest(`/widget/messenger/conversations/${conversationId}/typing`, {
|
|
849
|
+
method: 'POST',
|
|
850
|
+
headers: {
|
|
851
|
+
'Content-Type': 'application/json',
|
|
852
|
+
Authorization: `Bearer ${this.sessionToken}`,
|
|
853
|
+
},
|
|
854
|
+
body: JSON.stringify({ is_typing: isTyping }),
|
|
855
|
+
});
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
/**
|
|
859
|
+
* Mark conversation as read
|
|
860
|
+
* @param {string} conversationId - Conversation ID
|
|
861
|
+
* @returns {Promise<Object>} Response
|
|
862
|
+
*/
|
|
863
|
+
async markConversationAsRead(conversationId) {
|
|
864
|
+
if (!this.isSessionValid()) {
|
|
865
|
+
await this.init();
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
if (this.mock) {
|
|
869
|
+
return { status: true };
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
return this._makeRequest(`/widget/messenger/conversations/${conversationId}/read`, {
|
|
873
|
+
method: 'POST',
|
|
874
|
+
headers: { Authorization: `Bearer ${this.sessionToken}` },
|
|
875
|
+
});
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/**
|
|
879
|
+
* Get unread count
|
|
880
|
+
* @returns {Promise<Object>} Unread count data
|
|
881
|
+
*/
|
|
882
|
+
async getUnreadCount() {
|
|
883
|
+
if (!this.isSessionValid()) {
|
|
884
|
+
await this.init();
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
if (this.mock) {
|
|
888
|
+
const count = MOCK_CONVERSATIONS.reduce((sum, c) => sum + (c.unread || 0), 0);
|
|
889
|
+
return {
|
|
890
|
+
status: true,
|
|
891
|
+
data: { unread_count: count, unread_conversations: count > 0 ? 1 : 0 },
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
return this._makeRequest('/widget/messenger/unread', {
|
|
896
|
+
method: 'GET',
|
|
897
|
+
headers: { Authorization: `Bearer ${this.sessionToken}` },
|
|
898
|
+
});
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
/**
|
|
902
|
+
* Submit conversation rating
|
|
903
|
+
* @param {string} conversationId - Conversation ID
|
|
904
|
+
* @param {Object} data - Rating data
|
|
905
|
+
* @param {number} data.rating - Rating (1-5 or thumbs up/down)
|
|
906
|
+
* @param {string} data.comment - Optional comment
|
|
907
|
+
* @returns {Promise<Object>} Response
|
|
908
|
+
*/
|
|
909
|
+
async submitRating(conversationId, data) {
|
|
910
|
+
if (!this.isSessionValid()) {
|
|
911
|
+
await this.init();
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
if (this.mock) {
|
|
915
|
+
return { status: true, message: 'Thank you for your feedback!' };
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
return this._makeRequest(`/widget/messenger/conversations/${conversationId}/rate`, {
|
|
919
|
+
method: 'POST',
|
|
920
|
+
headers: {
|
|
921
|
+
'Content-Type': 'application/json',
|
|
922
|
+
Authorization: `Bearer ${this.sessionToken}`,
|
|
923
|
+
},
|
|
924
|
+
body: JSON.stringify({
|
|
925
|
+
rating: data.rating,
|
|
926
|
+
comment: data.comment || '',
|
|
927
|
+
}),
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
/**
|
|
932
|
+
* Identify contact (for logged-in users)
|
|
933
|
+
* @param {Object} data - Contact data
|
|
934
|
+
* @param {string} data.email - Email address
|
|
935
|
+
* @param {string} data.name - Name
|
|
936
|
+
* @returns {Promise<Object>} Response
|
|
937
|
+
*/
|
|
938
|
+
async identifyContact(data) {
|
|
939
|
+
if (!this.isSessionValid()) {
|
|
940
|
+
await this.init();
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
if (this.mock) {
|
|
944
|
+
return { status: true, message: 'Contact identified' };
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
return this._makeRequest('/widget/messenger/identify', {
|
|
948
|
+
method: 'POST',
|
|
949
|
+
headers: {
|
|
950
|
+
'Content-Type': 'application/json',
|
|
951
|
+
Authorization: `Bearer ${this.sessionToken}`,
|
|
952
|
+
},
|
|
953
|
+
body: JSON.stringify(data),
|
|
954
|
+
});
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
// ==========================================
|
|
958
|
+
// HELP ARTICLES ENDPOINTS
|
|
959
|
+
// ==========================================
|
|
960
|
+
|
|
961
|
+
/**
|
|
962
|
+
* Get help collections
|
|
963
|
+
* @param {Object} options - Query options
|
|
964
|
+
* @returns {Promise<Object>} Collections list
|
|
965
|
+
*/
|
|
966
|
+
async getHelpCollections(options = {}) {
|
|
967
|
+
if (!this.isSessionValid()) {
|
|
968
|
+
await this.init();
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
if (this.mock) {
|
|
972
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
973
|
+
return { status: true, data: MOCK_HELP_COLLECTIONS };
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
const params = new URLSearchParams();
|
|
977
|
+
if (options.limit) params.append('limit', options.limit);
|
|
978
|
+
|
|
979
|
+
const endpoint = `/widget/help/collections${params.toString() ? '?' + params.toString() : ''}`;
|
|
980
|
+
return this._makeRequest(endpoint, {
|
|
981
|
+
method: 'GET',
|
|
982
|
+
headers: { Authorization: `Bearer ${this.sessionToken}` },
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
/**
|
|
987
|
+
* Search help articles
|
|
988
|
+
* @param {string} query - Search query
|
|
989
|
+
* @param {Object} options - Query options
|
|
990
|
+
* @returns {Promise<Object>} Search results
|
|
991
|
+
*/
|
|
992
|
+
async searchHelpArticles(query, options = {}) {
|
|
993
|
+
if (!this.isSessionValid()) {
|
|
994
|
+
await this.init();
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
if (this.mock) {
|
|
998
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
999
|
+
const filtered = MOCK_HELP_COLLECTIONS.filter(
|
|
1000
|
+
(c) =>
|
|
1001
|
+
c.title.toLowerCase().includes(query.toLowerCase()) ||
|
|
1002
|
+
c.description.toLowerCase().includes(query.toLowerCase())
|
|
1003
|
+
);
|
|
1004
|
+
return { status: true, data: filtered };
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
const params = new URLSearchParams({ q: query });
|
|
1008
|
+
if (options.limit) params.append('limit', options.limit);
|
|
1009
|
+
|
|
1010
|
+
return this._makeRequest(`/widget/help/search?${params.toString()}`, {
|
|
1011
|
+
method: 'GET',
|
|
1012
|
+
headers: { Authorization: `Bearer ${this.sessionToken}` },
|
|
1013
|
+
});
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
// ==========================================
|
|
1017
|
+
// CHANGELOG ENDPOINTS
|
|
1018
|
+
// ==========================================
|
|
1019
|
+
|
|
1020
|
+
/**
|
|
1021
|
+
* Get published changelogs
|
|
1022
|
+
* @param {Object} options - Optional query parameters
|
|
1023
|
+
* @param {number} options.limit - Number of changelogs to fetch
|
|
1024
|
+
* @param {number} options.offset - Offset for pagination
|
|
1025
|
+
* @returns {Promise<Object>} Changelogs response
|
|
1026
|
+
*/
|
|
1027
|
+
async getChangelogs(options = {}) {
|
|
1028
|
+
if (!this.isSessionValid()) {
|
|
1029
|
+
await this.init();
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
if (!this.sessionToken) {
|
|
1033
|
+
throw new APIError(401, 'No valid session token available');
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
// Mock mode - return mock changelogs
|
|
1037
|
+
if (this.mock) {
|
|
1038
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1039
|
+
return {
|
|
1040
|
+
success: true,
|
|
1041
|
+
data: MOCK_CHANGELOGS,
|
|
1042
|
+
};
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
try {
|
|
1046
|
+
const queryParams = new URLSearchParams();
|
|
1047
|
+
if (options.limit) queryParams.append('limit', options.limit);
|
|
1048
|
+
if (options.offset) queryParams.append('offset', options.offset);
|
|
1049
|
+
|
|
1050
|
+
const endpoint = `/widget/changelogs${queryParams.toString() ? '?' + queryParams.toString() : ''}`;
|
|
1051
|
+
|
|
1052
|
+
const response = await this._makeRequest(endpoint, {
|
|
1053
|
+
method: 'GET',
|
|
1054
|
+
headers: {
|
|
1055
|
+
Authorization: `Bearer ${this.sessionToken}`,
|
|
1056
|
+
},
|
|
1057
|
+
});
|
|
1058
|
+
|
|
1059
|
+
return response;
|
|
1060
|
+
} catch (error) {
|
|
1061
|
+
if (error.status === 401) {
|
|
1062
|
+
this.sessionToken = null;
|
|
1063
|
+
this.sessionExpiry = null;
|
|
1064
|
+
await this.init();
|
|
1065
|
+
return this.getChangelogs(options);
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
throw new APIError(
|
|
1069
|
+
error.status || 500,
|
|
1070
|
+
`Failed to get changelogs: ${error.message}`,
|
|
1071
|
+
error.response
|
|
1072
|
+
);
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
|
|
433
1076
|
isSessionValid() {
|
|
434
1077
|
return (
|
|
435
1078
|
this.sessionToken && this.sessionExpiry && new Date() < this.sessionExpiry
|