@product7/feedback-sdk 1.2.8 → 1.3.0
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/dist/feedback-sdk.js +1530 -1861
- 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/api/mock-data/index.js +202 -0
- package/src/api/services/ChangelogService.js +28 -0
- package/src/api/services/FeedbackService.js +44 -0
- package/src/api/services/HelpService.js +50 -0
- package/src/api/services/MessengerService.js +197 -0
- package/src/api/services/SurveyService.js +99 -0
- package/src/api/utils/helpers.js +30 -0
- package/src/core/APIService.js +40 -1142
- package/src/core/BaseAPIService.js +245 -0
- package/src/core/FeedbackSDK.js +0 -68
- package/src/styles/base.js +75 -0
- package/src/styles/changelog.js +698 -0
- package/src/styles/feedback.js +574 -0
- package/src/styles/styles.js +5 -1379
- package/src/widgets/MessengerWidget.js +1 -2
- package/src/widgets/messenger/components/MessengerLauncher.js +7 -7
- package/src/widgets/messenger/components/NavigationTabs.js +16 -4
- package/src/widgets/messenger/views/ChangelogView.js +9 -9
- package/src/widgets/messenger/views/ChatView.js +9 -20
- package/src/widgets/messenger/views/ConversationsView.js +9 -13
- package/src/widgets/messenger/views/HelpView.js +15 -14
- package/src/widgets/messenger/views/HomeView.js +6 -15
package/package.json
CHANGED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
export const MOCK_CHANGELOGS = [
|
|
2
|
+
{
|
|
3
|
+
id: 'changelog_1',
|
|
4
|
+
title:
|
|
5
|
+
'Feature prioritization, multiple roadmaps, and feature request analytics.',
|
|
6
|
+
excerpt:
|
|
7
|
+
'We are super excited to bring you the long-waited feature prioritization together with many quality updates!',
|
|
8
|
+
description:
|
|
9
|
+
'We are super excited to bring you the long-waited feature prioritization together with many quality updates!',
|
|
10
|
+
cover_image:
|
|
11
|
+
'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAwIiBoZWlnaHQ9IjI1MCIgdmlld0JveD0iMCAwIDQwMCAyNTAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHJlY3Qgd2lkdGg9IjQwMCIgaGVpZ2h0PSIyNTAiIGZpbGw9IiNEQkVBRkUiLz48cmVjdCB4PSIyMCIgeT0iMjAiIHdpZHRoPSIzNjAiIGhlaWdodD0iMjEwIiByeD0iOCIgZmlsbD0id2hpdGUiIHN0cm9rZT0iIzE1NUVFRiIgc3Ryb2tlLXdpZHRoPSIyIi8+PHJlY3QgeD0iMzAiIHk9IjQwIiB3aWR0aD0iODAiIGhlaWdodD0iOCIgcng9IjQiIGZpbGw9IiNFNUU3RUIiLz48cmVjdCB4PSIzMCIgeT0iNjAiIHdpZHRoPSIxNDAiIGhlaWdodD0iNDAiIHJ4PSI0IiBmaWxsPSIjRjNGNEY2Ii8+PHRleHQgeD0iNDAiIHk9Ijg1IiBmb250LWZhbWlseT0ic3lzdGVtLXVpIiBmb250LXNpemU9IjE2IiBmb250LXdlaWdodD0iNjAwIiBmaWxsPSIjMUYyOTM3Ij4yMDA8L3RleHQ+PHRleHQgeD0iNDAiIHk9Ijk1IiBmb250LWZhbWlseT0ic3lzdGVtLXVpIiBmb250LXNpemU9IjgiIGZpbGw9IiM2QjcyODAiPlBvc3RzPC90ZXh0PjxyZWN0IHg9IjE4MCIgeT0iNjAiIHdpZHRoPSI4MCIgaGVpZ2h0PSI0MCIgcng9IjQiIGZpbGw9IiNGM0Y0RjYiLz48cmVjdCB4PSIyNzAiIHk9IjYwIiB3aWR0aD0iODAiIGhlaWdodD0iNDAiIHJ4PSI0IiBmaWxsPSIjRjNGNEY2Ii8+PHBhdGggZD0iTTMwIDEzMEgzNzBNMzAgMTUwSDM3ME0zMCAxNzBIMzcwTTMwIDE5MEgzNzAiIHN0cm9rZT0iI0U1RTdFQiIgc3Ryb2tlLXdpZHRoPSIxIi8+PHBhdGggZD0iTTUwIDE2MEwxMDAgMTQwTDE1MCAxNTBMMjAwIDEzMEwyNTAgMTM1TDMwMCAxMjBMMzUwIDEyNSIgc3Ryb2tlPSIjMTU1RUVGIiBzdHJva2Utd2lkdGg9IjIiIGZpbGw9Im5vbmUiLz48L3N2Zz4=',
|
|
12
|
+
slug: 'feature-prioritization-roadmaps-analytics',
|
|
13
|
+
published_at: '2025-03-15T10:00:00Z',
|
|
14
|
+
labels: [
|
|
15
|
+
{ name: 'New Feature', color: '#10B981' },
|
|
16
|
+
{ name: 'Analytics', color: '#6366F1' },
|
|
17
|
+
],
|
|
18
|
+
status: 'published',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
id: 'changelog_2',
|
|
22
|
+
title: 'Dark mode support and UI improvements',
|
|
23
|
+
excerpt:
|
|
24
|
+
'You asked for it, we delivered! Dark mode is now available across all pages.',
|
|
25
|
+
description:
|
|
26
|
+
'You asked for it, we delivered! Dark mode is now available across all pages.',
|
|
27
|
+
cover_image:
|
|
28
|
+
'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAwIiBoZWlnaHQ9IjI1MCIgdmlld0JveD0iMCAwIDQwMCAyNTAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHJlY3Qgd2lkdGg9IjQwMCIgaGVpZ2h0PSIyNTAiIGZpbGw9IiMxRjI5MzciLz48cmVjdCB4PSIyMCIgeT0iMjAiIHdpZHRoPSIzNjAiIGhlaWdodD0iMjEwIiByeD0iOCIgZmlsbD0iIzM3NDE1MSIgc3Ryb2tlPSIjNEI1NTYzIiBzdHJva2Utd2lkdGg9IjEiLz48Y2lyY2xlIGN4PSIyMDAiIGN5PSIxMjUiIHI9IjQwIiBmaWxsPSIjRkJCRjI0Ii8+PHBhdGggZD0iTTIyMCAxMjVDMjIwIDEzNiAyMTEgMTQ1IDIwMCAxNDVDMTg5IDE0NSAxODAgMTM2IDE4MCAxMjVDMTgwIDExNCAxODkgMTA1IDIwMCAxMDVDMjExIDEwNSAyMjAgMTE0IDIyMCAxMjVaIiBmaWxsPSIjMUYyOTM3Ii8+PC9zdmc+',
|
|
29
|
+
slug: 'dark-mode-ui-improvements',
|
|
30
|
+
published_at: '2025-03-01T10:00:00Z',
|
|
31
|
+
labels: [{ name: 'Enhancement', color: '#8B5CF6' }],
|
|
32
|
+
status: 'published',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: 'changelog_3',
|
|
36
|
+
title: 'Performance improvements and bug fixes',
|
|
37
|
+
excerpt:
|
|
38
|
+
'This release includes major performance improvements and several bug fixes.',
|
|
39
|
+
description:
|
|
40
|
+
'This release includes major performance improvements and several bug fixes.',
|
|
41
|
+
slug: 'performance-improvements-bug-fixes',
|
|
42
|
+
published_at: '2025-02-15T10:00:00Z',
|
|
43
|
+
labels: [
|
|
44
|
+
{ name: 'Bug Fix', color: '#EF4444' },
|
|
45
|
+
{ name: 'Performance', color: '#F59E0B' },
|
|
46
|
+
],
|
|
47
|
+
status: 'published',
|
|
48
|
+
},
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
export const MOCK_CONVERSATIONS = [
|
|
52
|
+
{
|
|
53
|
+
id: 'conv_1',
|
|
54
|
+
subject: 'Question about pricing',
|
|
55
|
+
status: 'open',
|
|
56
|
+
last_message_at: new Date(Date.now() - 49 * 60 * 1000).toISOString(),
|
|
57
|
+
created_at: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString(),
|
|
58
|
+
unread: 1,
|
|
59
|
+
assigned_user: {
|
|
60
|
+
id: 'user_1',
|
|
61
|
+
name: 'Sarah',
|
|
62
|
+
avatar: null,
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
id: 'conv_2',
|
|
67
|
+
subject: 'Feature request',
|
|
68
|
+
status: 'open',
|
|
69
|
+
last_message_at: new Date(
|
|
70
|
+
Date.now() - 6 * 24 * 60 * 60 * 1000
|
|
71
|
+
).toISOString(),
|
|
72
|
+
created_at: new Date(Date.now() - 10 * 24 * 60 * 60 * 1000).toISOString(),
|
|
73
|
+
unread: 0,
|
|
74
|
+
assigned_user: {
|
|
75
|
+
id: 'user_2',
|
|
76
|
+
name: 'Tom',
|
|
77
|
+
avatar: null,
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
export const MOCK_MESSAGES = {
|
|
83
|
+
conv_1: [
|
|
84
|
+
{
|
|
85
|
+
id: 'msg_1',
|
|
86
|
+
content: "Hi there! 👋 I'm Sarah. How can I help you today?",
|
|
87
|
+
sender_type: 'agent',
|
|
88
|
+
sender_name: 'Sarah',
|
|
89
|
+
sender_avatar: null,
|
|
90
|
+
created_at: new Date(Date.now() - 50 * 60 * 1000).toISOString(),
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
id: 'msg_2',
|
|
94
|
+
content: 'Hi! I have a question about your enterprise pricing.',
|
|
95
|
+
sender_type: 'customer',
|
|
96
|
+
created_at: new Date(Date.now() - 49 * 60 * 1000).toISOString(),
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
conv_2: [
|
|
100
|
+
{
|
|
101
|
+
id: 'msg_3',
|
|
102
|
+
content: "Hello! I'm Tom from the product team.",
|
|
103
|
+
sender_type: 'agent',
|
|
104
|
+
sender_name: 'Tom',
|
|
105
|
+
sender_avatar: null,
|
|
106
|
+
created_at: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(),
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: 'msg_4',
|
|
110
|
+
content: 'I would love to see a dark mode feature!',
|
|
111
|
+
sender_type: 'customer',
|
|
112
|
+
created_at: new Date(
|
|
113
|
+
Date.now() - 6 * 24 * 60 * 60 * 1000 - 30 * 60 * 1000
|
|
114
|
+
).toISOString(),
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
id: 'msg_5',
|
|
118
|
+
content:
|
|
119
|
+
"Great suggestion! That feature will be available next week. I'll let you know when it's ready.",
|
|
120
|
+
sender_type: 'agent',
|
|
121
|
+
sender_name: 'Tom',
|
|
122
|
+
sender_avatar: null,
|
|
123
|
+
created_at: new Date(Date.now() - 6 * 24 * 60 * 60 * 1000).toISOString(),
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
export const MOCK_HELP_COLLECTIONS = [
|
|
129
|
+
{
|
|
130
|
+
id: 'collection_1',
|
|
131
|
+
title: 'Product Overview',
|
|
132
|
+
description: 'See how your AI-first customer service solution works.',
|
|
133
|
+
articleCount: 24,
|
|
134
|
+
icon: 'ph-book-open',
|
|
135
|
+
url: '#',
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
id: 'collection_2',
|
|
139
|
+
title: 'Getting Started',
|
|
140
|
+
description: 'Everything you need to know to get started with Product7.',
|
|
141
|
+
articleCount: 30,
|
|
142
|
+
icon: 'ph-rocket',
|
|
143
|
+
url: '#',
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
id: 'collection_3',
|
|
147
|
+
title: 'AI Agent',
|
|
148
|
+
description:
|
|
149
|
+
'Resolving customer questions instantly and accurately—from live chat to email.',
|
|
150
|
+
articleCount: 82,
|
|
151
|
+
icon: 'ph-robot',
|
|
152
|
+
url: '#',
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
id: 'collection_4',
|
|
156
|
+
title: 'Channels',
|
|
157
|
+
description:
|
|
158
|
+
'Enabling the channels you use to communicate with customers, all from the Inbox.',
|
|
159
|
+
articleCount: 45,
|
|
160
|
+
icon: 'ph-chat-circle',
|
|
161
|
+
url: '#',
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
id: 'collection_5',
|
|
165
|
+
title: 'Billing & Payments',
|
|
166
|
+
description: 'Manage your subscription, invoices, and payment methods.',
|
|
167
|
+
articleCount: 12,
|
|
168
|
+
icon: 'ph-credit-card',
|
|
169
|
+
url: '#',
|
|
170
|
+
},
|
|
171
|
+
];
|
|
172
|
+
|
|
173
|
+
export const MOCK_SURVEYS = [
|
|
174
|
+
{
|
|
175
|
+
id: 'mock_nps_survey',
|
|
176
|
+
type: 'nps',
|
|
177
|
+
title: 'How likely are you to recommend us?',
|
|
178
|
+
description: 'Your feedback helps us improve',
|
|
179
|
+
low_label: 'Not likely',
|
|
180
|
+
high_label: 'Very likely',
|
|
181
|
+
trigger: 'manual',
|
|
182
|
+
status: 'active',
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
id: 'mock_csat_survey',
|
|
186
|
+
type: 'csat',
|
|
187
|
+
title: 'How satisfied are you?',
|
|
188
|
+
description: 'Rate your experience with our product',
|
|
189
|
+
trigger: 'manual',
|
|
190
|
+
status: 'active',
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
id: 'mock_ces_survey',
|
|
194
|
+
type: 'ces',
|
|
195
|
+
title: 'How easy was it?',
|
|
196
|
+
description: 'Rate the ease of completing your task',
|
|
197
|
+
low_label: 'Very difficult',
|
|
198
|
+
high_label: 'Very easy',
|
|
199
|
+
trigger: 'manual',
|
|
200
|
+
status: 'active',
|
|
201
|
+
},
|
|
202
|
+
];
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { MOCK_CHANGELOGS } from '../mock-data';
|
|
2
|
+
import { delay } from '../utils/helpers.js';
|
|
3
|
+
|
|
4
|
+
export class ChangelogService {
|
|
5
|
+
constructor(baseAPI) {
|
|
6
|
+
this.api = baseAPI;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
async getChangelogs(options = {}) {
|
|
10
|
+
await this.api._ensureSession();
|
|
11
|
+
|
|
12
|
+
if (this.api.mock) {
|
|
13
|
+
await delay(300);
|
|
14
|
+
return { success: true, data: MOCK_CHANGELOGS };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return this.api._handleAuthRetry(async () => {
|
|
18
|
+
const endpoint = this.api._getEndpointWithParams(
|
|
19
|
+
'/widget/changelogs',
|
|
20
|
+
options
|
|
21
|
+
);
|
|
22
|
+
return await this.api._makeRequest(endpoint, {
|
|
23
|
+
method: 'GET',
|
|
24
|
+
headers: { Authorization: `Bearer ${this.api.sessionToken}` },
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { delay } from '../utils/helpers.js';
|
|
2
|
+
|
|
3
|
+
export class FeedbackService {
|
|
4
|
+
constructor(baseAPI) {
|
|
5
|
+
this.api = baseAPI;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
async submitFeedback(feedbackData) {
|
|
9
|
+
await this.api._ensureSession();
|
|
10
|
+
|
|
11
|
+
if (this.api.mock) {
|
|
12
|
+
await delay(500);
|
|
13
|
+
return {
|
|
14
|
+
success: true,
|
|
15
|
+
data: {
|
|
16
|
+
id: 'mock_post_' + Date.now(),
|
|
17
|
+
title: feedbackData.title,
|
|
18
|
+
content: feedbackData.content,
|
|
19
|
+
},
|
|
20
|
+
message: 'Feedback submitted successfully!',
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const payload = {
|
|
25
|
+
board:
|
|
26
|
+
feedbackData.board_id || feedbackData.board || feedbackData.boardId,
|
|
27
|
+
title: feedbackData.title,
|
|
28
|
+
content: feedbackData.content,
|
|
29
|
+
attachments: feedbackData.attachments || [],
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
return this.api._handleAuthRetry(async () => {
|
|
33
|
+
const response = await this.api._makeRequest('/widget/feedback', {
|
|
34
|
+
method: 'POST',
|
|
35
|
+
body: JSON.stringify(payload),
|
|
36
|
+
headers: {
|
|
37
|
+
'Content-Type': 'application/json',
|
|
38
|
+
Authorization: `Bearer ${this.api.sessionToken}`,
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
return response;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { MOCK_HELP_COLLECTIONS } from '../mock-data';
|
|
2
|
+
import { delay } from '../utils/helpers.js';
|
|
3
|
+
|
|
4
|
+
export class HelpService {
|
|
5
|
+
constructor(baseAPI) {
|
|
6
|
+
this.api = baseAPI;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
async getHelpCollections(options = {}) {
|
|
10
|
+
await this.api._ensureSession();
|
|
11
|
+
|
|
12
|
+
if (this.api.mock) {
|
|
13
|
+
await delay(200);
|
|
14
|
+
return { status: true, data: MOCK_HELP_COLLECTIONS };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const endpoint = this.api._getEndpointWithParams(
|
|
18
|
+
'/widget/help/collections',
|
|
19
|
+
options
|
|
20
|
+
);
|
|
21
|
+
return this.api._makeRequest(endpoint, {
|
|
22
|
+
method: 'GET',
|
|
23
|
+
headers: { Authorization: `Bearer ${this.api.sessionToken}` },
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async searchHelpArticles(query, options = {}) {
|
|
28
|
+
await this.api._ensureSession();
|
|
29
|
+
|
|
30
|
+
if (this.api.mock) {
|
|
31
|
+
await delay(200);
|
|
32
|
+
const filtered = MOCK_HELP_COLLECTIONS.filter(
|
|
33
|
+
(c) =>
|
|
34
|
+
c.title.toLowerCase().includes(query.toLowerCase()) ||
|
|
35
|
+
c.description.toLowerCase().includes(query.toLowerCase())
|
|
36
|
+
);
|
|
37
|
+
return { status: true, data: filtered };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const params = { q: query, ...options };
|
|
41
|
+
const endpoint = this.api._getEndpointWithParams(
|
|
42
|
+
'/widget/help/search',
|
|
43
|
+
params
|
|
44
|
+
);
|
|
45
|
+
return this.api._makeRequest(endpoint, {
|
|
46
|
+
method: 'GET',
|
|
47
|
+
headers: { Authorization: `Bearer ${this.api.sessionToken}` },
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { MOCK_CONVERSATIONS, MOCK_MESSAGES } from '../mock-data';
|
|
2
|
+
import { delay } from '../utils/helpers.js';
|
|
3
|
+
|
|
4
|
+
export class MessengerService {
|
|
5
|
+
constructor(baseAPI) {
|
|
6
|
+
this.api = baseAPI;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
async getMessengerSettings() {
|
|
10
|
+
await this.api._ensureSession();
|
|
11
|
+
|
|
12
|
+
if (this.api.mock) {
|
|
13
|
+
return {
|
|
14
|
+
status: true,
|
|
15
|
+
data: {
|
|
16
|
+
enabled: true,
|
|
17
|
+
greeting_message: 'Hi there! How can we help you today?',
|
|
18
|
+
team_name: 'Support Team',
|
|
19
|
+
response_time: 'Usually replies within a few minutes',
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return this.api._makeRequest('/widget/messenger/settings', {
|
|
25
|
+
method: 'GET',
|
|
26
|
+
headers: { Authorization: `Bearer ${this.api.sessionToken}` },
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async checkAgentsOnline() {
|
|
31
|
+
await this.api._ensureSession();
|
|
32
|
+
|
|
33
|
+
if (this.api.mock) {
|
|
34
|
+
return {
|
|
35
|
+
status: true,
|
|
36
|
+
data: {
|
|
37
|
+
agents_online: true,
|
|
38
|
+
online_count: 2,
|
|
39
|
+
response_time: 'Usually replies within a few minutes',
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return this.api._makeRequest('/widget/messenger/agents/online', {
|
|
45
|
+
method: 'GET',
|
|
46
|
+
headers: { Authorization: `Bearer ${this.api.sessionToken}` },
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async getConversations(options = {}) {
|
|
51
|
+
await this.api._ensureSession();
|
|
52
|
+
|
|
53
|
+
if (this.api.mock) {
|
|
54
|
+
await delay(300);
|
|
55
|
+
return {
|
|
56
|
+
status: true,
|
|
57
|
+
data: MOCK_CONVERSATIONS,
|
|
58
|
+
meta: { total: MOCK_CONVERSATIONS.length, page: 1, limit: 20 },
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const endpoint = this.api._getEndpointWithParams(
|
|
63
|
+
'/widget/messenger/conversations',
|
|
64
|
+
options
|
|
65
|
+
);
|
|
66
|
+
return this.api._makeRequest(endpoint, {
|
|
67
|
+
method: 'GET',
|
|
68
|
+
headers: { Authorization: `Bearer ${this.api.sessionToken}` },
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async getConversation(conversationId) {
|
|
73
|
+
await this.api._ensureSession();
|
|
74
|
+
|
|
75
|
+
if (this.api.mock) {
|
|
76
|
+
await delay(200);
|
|
77
|
+
const conv = MOCK_CONVERSATIONS.find((c) => c.id === conversationId);
|
|
78
|
+
return {
|
|
79
|
+
status: true,
|
|
80
|
+
data: { ...conv, messages: MOCK_MESSAGES[conversationId] || [] },
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return this.api._makeRequest(
|
|
85
|
+
`/widget/messenger/conversations/${conversationId}`,
|
|
86
|
+
{
|
|
87
|
+
method: 'GET',
|
|
88
|
+
headers: { Authorization: `Bearer ${this.api.sessionToken}` },
|
|
89
|
+
}
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async startConversation(data) {
|
|
94
|
+
await this.api._ensureSession();
|
|
95
|
+
|
|
96
|
+
if (this.api.mock) {
|
|
97
|
+
await delay(300);
|
|
98
|
+
const newConv = {
|
|
99
|
+
id: 'conv_' + Date.now(),
|
|
100
|
+
subject: data.subject || 'New conversation',
|
|
101
|
+
status: 'open',
|
|
102
|
+
last_message_at: new Date().toISOString(),
|
|
103
|
+
created_at: new Date().toISOString(),
|
|
104
|
+
messages: [
|
|
105
|
+
{
|
|
106
|
+
id: 'msg_' + Date.now(),
|
|
107
|
+
content: data.message,
|
|
108
|
+
sender_type: 'customer',
|
|
109
|
+
created_at: new Date().toISOString(),
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
};
|
|
113
|
+
MOCK_CONVERSATIONS.unshift(newConv);
|
|
114
|
+
MOCK_MESSAGES[newConv.id] = newConv.messages;
|
|
115
|
+
return { status: true, data: newConv };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return this.api._makeRequest('/widget/messenger/conversations', {
|
|
119
|
+
method: 'POST',
|
|
120
|
+
headers: {
|
|
121
|
+
'Content-Type': 'application/json',
|
|
122
|
+
Authorization: `Bearer ${this.api.sessionToken}`,
|
|
123
|
+
},
|
|
124
|
+
body: JSON.stringify({
|
|
125
|
+
message: data.message,
|
|
126
|
+
subject: data.subject || '',
|
|
127
|
+
}),
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async sendMessage(conversationId, data) {
|
|
132
|
+
await this.api._ensureSession();
|
|
133
|
+
|
|
134
|
+
if (this.api.mock) {
|
|
135
|
+
await delay(200);
|
|
136
|
+
const newMessage = {
|
|
137
|
+
id: 'msg_' + Date.now(),
|
|
138
|
+
content: data.content,
|
|
139
|
+
sender_type: 'customer',
|
|
140
|
+
created_at: new Date().toISOString(),
|
|
141
|
+
};
|
|
142
|
+
if (!MOCK_MESSAGES[conversationId]) {
|
|
143
|
+
MOCK_MESSAGES[conversationId] = [];
|
|
144
|
+
}
|
|
145
|
+
MOCK_MESSAGES[conversationId].push(newMessage);
|
|
146
|
+
return { status: true, data: newMessage };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return this.api._makeRequest(
|
|
150
|
+
`/widget/messenger/conversations/${conversationId}/messages`,
|
|
151
|
+
{
|
|
152
|
+
method: 'POST',
|
|
153
|
+
headers: {
|
|
154
|
+
'Content-Type': 'application/json',
|
|
155
|
+
Authorization: `Bearer ${this.api.sessionToken}`,
|
|
156
|
+
},
|
|
157
|
+
body: JSON.stringify({ content: data.content }),
|
|
158
|
+
}
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async getUnreadCount() {
|
|
163
|
+
await this.api._ensureSession();
|
|
164
|
+
|
|
165
|
+
if (this.api.mock) {
|
|
166
|
+
const count = MOCK_CONVERSATIONS.reduce(
|
|
167
|
+
(sum, c) => sum + (c.unread || 0),
|
|
168
|
+
0
|
|
169
|
+
);
|
|
170
|
+
return {
|
|
171
|
+
status: true,
|
|
172
|
+
data: { unread_count: count, unread_conversations: count > 0 ? 1 : 0 },
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return this.api._makeRequest('/widget/messenger/unread', {
|
|
177
|
+
method: 'GET',
|
|
178
|
+
headers: { Authorization: `Bearer ${this.api.sessionToken}` },
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async markConversationAsRead(conversationId) {
|
|
183
|
+
await this.api._ensureSession();
|
|
184
|
+
|
|
185
|
+
if (this.api.mock) {
|
|
186
|
+
return { status: true };
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return this.api._makeRequest(
|
|
190
|
+
`/widget/messenger/conversations/${conversationId}/read`,
|
|
191
|
+
{
|
|
192
|
+
method: 'POST',
|
|
193
|
+
headers: { Authorization: `Bearer ${this.api.sessionToken}` },
|
|
194
|
+
}
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { APIError } from '../../utils/errors.js';
|
|
2
|
+
import { MOCK_SURVEYS } from '../mock-data';
|
|
3
|
+
import { delay, getDeviceInfo } from '../utils/helpers.js';
|
|
4
|
+
|
|
5
|
+
export class SurveyService {
|
|
6
|
+
constructor(baseAPI) {
|
|
7
|
+
this.api = baseAPI;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async getActiveSurveys(context = {}) {
|
|
11
|
+
await this.api._ensureSession();
|
|
12
|
+
|
|
13
|
+
if (this.api.mock) {
|
|
14
|
+
await delay(200);
|
|
15
|
+
return { success: true, data: MOCK_SURVEYS };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const params = {
|
|
19
|
+
url:
|
|
20
|
+
context.url ||
|
|
21
|
+
(typeof window !== 'undefined' ? window.location.href : ''),
|
|
22
|
+
...getDeviceInfo(),
|
|
23
|
+
...(context.userProperties && {
|
|
24
|
+
user_properties: context.userProperties,
|
|
25
|
+
}),
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return this.api._handleAuthRetry(async () => {
|
|
29
|
+
const endpoint = this.api._getEndpointWithParams(
|
|
30
|
+
'/widget/surveys/active',
|
|
31
|
+
params
|
|
32
|
+
);
|
|
33
|
+
return await this.api._makeRequest(endpoint, {
|
|
34
|
+
method: 'GET',
|
|
35
|
+
headers: { Authorization: `Bearer ${this.api.sessionToken}` },
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async submitSurveyResponse(surveyId, responseData) {
|
|
41
|
+
if (!surveyId) throw new APIError(400, 'Survey ID is required');
|
|
42
|
+
|
|
43
|
+
await this.api._ensureSession();
|
|
44
|
+
|
|
45
|
+
if (this.api.mock) {
|
|
46
|
+
await delay(300);
|
|
47
|
+
return {
|
|
48
|
+
success: true,
|
|
49
|
+
data: {
|
|
50
|
+
id: 'mock_response_' + Date.now(),
|
|
51
|
+
survey_id: surveyId,
|
|
52
|
+
...responseData,
|
|
53
|
+
},
|
|
54
|
+
message: 'Survey response submitted successfully!',
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const payload = {
|
|
59
|
+
rating: responseData.rating,
|
|
60
|
+
feedback: responseData.feedback || '',
|
|
61
|
+
answers: responseData.answers || {},
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
return this.api._handleAuthRetry(async () => {
|
|
65
|
+
return await this.api._makeRequest(
|
|
66
|
+
`/widget/surveys/${surveyId}/responses`,
|
|
67
|
+
{
|
|
68
|
+
method: 'POST',
|
|
69
|
+
body: JSON.stringify(payload),
|
|
70
|
+
headers: {
|
|
71
|
+
'Content-Type': 'application/json',
|
|
72
|
+
Authorization: `Bearer ${this.api.sessionToken}`,
|
|
73
|
+
},
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async dismissSurvey(surveyId) {
|
|
80
|
+
if (!surveyId) throw new APIError(400, 'Survey ID is required');
|
|
81
|
+
|
|
82
|
+
await this.api._ensureSession();
|
|
83
|
+
|
|
84
|
+
if (this.api.mock) {
|
|
85
|
+
await delay(100);
|
|
86
|
+
return { success: true, message: 'Survey dismissed successfully' };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return this.api._handleAuthRetry(async () => {
|
|
90
|
+
return await this.api._makeRequest(
|
|
91
|
+
`/widget/surveys/${surveyId}/dismiss`,
|
|
92
|
+
{
|
|
93
|
+
method: 'POST',
|
|
94
|
+
headers: { Authorization: `Bearer ${this.api.sessionToken}` },
|
|
95
|
+
}
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
2
|
+
|
|
3
|
+
export function getDeviceInfo() {
|
|
4
|
+
if (typeof navigator === 'undefined') {
|
|
5
|
+
return { device: null, browser: null, os: null };
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const ua = navigator.userAgent;
|
|
9
|
+
|
|
10
|
+
let device = 'desktop';
|
|
11
|
+
if (/Mobi|Android/i.test(ua)) {
|
|
12
|
+
device = /Tablet|iPad/i.test(ua) ? 'tablet' : 'mobile';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let browser = 'unknown';
|
|
16
|
+
if (ua.includes('Firefox')) browser = 'firefox';
|
|
17
|
+
else if (ua.includes('Edg')) browser = 'edge';
|
|
18
|
+
else if (ua.includes('Chrome')) browser = 'chrome';
|
|
19
|
+
else if (ua.includes('Safari')) browser = 'safari';
|
|
20
|
+
else if (ua.includes('Opera') || ua.includes('OPR')) browser = 'opera';
|
|
21
|
+
|
|
22
|
+
let os = 'unknown';
|
|
23
|
+
if (ua.includes('Windows')) os = 'windows';
|
|
24
|
+
else if (ua.includes('Mac')) os = 'macos';
|
|
25
|
+
else if (ua.includes('Linux')) os = 'linux';
|
|
26
|
+
else if (ua.includes('Android')) os = 'android';
|
|
27
|
+
else if (ua.includes('iPhone') || ua.includes('iPad')) os = 'ios';
|
|
28
|
+
|
|
29
|
+
return { device, browser, os };
|
|
30
|
+
}
|