@product7/product7-js 0.1.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.
Files changed (58) hide show
  1. package/README.md +1025 -0
  2. package/dist/README.md +1025 -0
  3. package/dist/product7-js.js +14658 -0
  4. package/dist/product7-js.js.map +1 -0
  5. package/dist/product7-js.min.js +2 -0
  6. package/dist/product7-js.min.js.map +1 -0
  7. package/package.json +114 -0
  8. package/src/api/mock-data/index.js +360 -0
  9. package/src/api/services/ChangelogService.js +28 -0
  10. package/src/api/services/FeedbackService.js +44 -0
  11. package/src/api/services/HelpService.js +50 -0
  12. package/src/api/services/MessengerService.js +279 -0
  13. package/src/api/services/SurveyService.js +127 -0
  14. package/src/api/utils/helpers.js +30 -0
  15. package/src/core/APIService.js +303 -0
  16. package/src/core/BaseAPIService.js +298 -0
  17. package/src/core/EventBus.js +54 -0
  18. package/src/core/Product7.js +812 -0
  19. package/src/core/WebSocketService.js +275 -0
  20. package/src/docs/api.md +226 -0
  21. package/src/docs/example.md +461 -0
  22. package/src/docs/framework-integrations.md +714 -0
  23. package/src/docs/installation.md +281 -0
  24. package/src/index.js +894 -0
  25. package/src/styles/base.js +50 -0
  26. package/src/styles/changelog.js +665 -0
  27. package/src/styles/components.js +553 -0
  28. package/src/styles/design-tokens.js +124 -0
  29. package/src/styles/feedback.js +325 -0
  30. package/src/styles/messenger-components.js +632 -0
  31. package/src/styles/messenger-core.js +233 -0
  32. package/src/styles/messenger-features.js +169 -0
  33. package/src/styles/messenger-views.js +877 -0
  34. package/src/styles/messenger.js +17 -0
  35. package/src/styles/messengerCustomStyles.js +114 -0
  36. package/src/styles/styles.js +26 -0
  37. package/src/styles/survey.js +894 -0
  38. package/src/utils/errors.js +142 -0
  39. package/src/utils/helpers.js +219 -0
  40. package/src/widgets/BaseWidget.js +548 -0
  41. package/src/widgets/ButtonWidget.js +104 -0
  42. package/src/widgets/ChangelogWidget.js +615 -0
  43. package/src/widgets/InlineWidget.js +148 -0
  44. package/src/widgets/MessengerWidget.js +979 -0
  45. package/src/widgets/SurveyWidget.js +1325 -0
  46. package/src/widgets/TabWidget.js +45 -0
  47. package/src/widgets/WidgetFactory.js +70 -0
  48. package/src/widgets/messenger/MessengerState.js +323 -0
  49. package/src/widgets/messenger/components/MessengerLauncher.js +124 -0
  50. package/src/widgets/messenger/components/MessengerPanel.js +111 -0
  51. package/src/widgets/messenger/components/NavigationTabs.js +130 -0
  52. package/src/widgets/messenger/views/ChangelogView.js +167 -0
  53. package/src/widgets/messenger/views/ChatView.js +592 -0
  54. package/src/widgets/messenger/views/ConversationsView.js +244 -0
  55. package/src/widgets/messenger/views/HelpView.js +239 -0
  56. package/src/widgets/messenger/views/HomeView.js +300 -0
  57. package/src/widgets/messenger/views/PreChatFormView.js +109 -0
  58. package/types/index.d.ts +341 -0
@@ -0,0 +1,279 @@
1
+ import { MOCK_CONVERSATIONS, MOCK_MESSAGES } from '../mock-data/index.js';
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?',
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
+ attachments: data.attachments || [],
111
+ },
112
+ ],
113
+ };
114
+ MOCK_CONVERSATIONS.unshift(newConv);
115
+ MOCK_MESSAGES[newConv.id] = newConv.messages;
116
+ return { status: true, data: newConv };
117
+ }
118
+
119
+ return this.api._makeRequest('/widget/messenger/conversations', {
120
+ method: 'POST',
121
+ headers: {
122
+ 'Content-Type': 'application/json',
123
+ Authorization: `Bearer ${this.api.sessionToken}`,
124
+ },
125
+ body: JSON.stringify({
126
+ message: data.message,
127
+ subject: data.subject || '',
128
+ attachments: data.attachments || [],
129
+ }),
130
+ });
131
+ }
132
+
133
+ async sendMessage(conversationId, data) {
134
+ await this.api._ensureSession();
135
+
136
+ if (this.api.mock) {
137
+ await delay(200);
138
+ const newMessage = {
139
+ id: 'msg_' + Date.now(),
140
+ content: data.content,
141
+ sender_type: 'customer',
142
+ created_at: new Date().toISOString(),
143
+ attachments: data.attachments || [],
144
+ };
145
+ if (!MOCK_MESSAGES[conversationId]) {
146
+ MOCK_MESSAGES[conversationId] = [];
147
+ }
148
+ MOCK_MESSAGES[conversationId].push(newMessage);
149
+ return { status: true, data: newMessage };
150
+ }
151
+
152
+ return this.api._makeRequest(
153
+ `/widget/messenger/conversations/${conversationId}/messages`,
154
+ {
155
+ method: 'POST',
156
+ headers: {
157
+ 'Content-Type': 'application/json',
158
+ Authorization: `Bearer ${this.api.sessionToken}`,
159
+ },
160
+ body: JSON.stringify({
161
+ content: data.content,
162
+ attachments: data.attachments || [],
163
+ }),
164
+ }
165
+ );
166
+ }
167
+
168
+ async getUnreadCount() {
169
+ await this.api._ensureSession();
170
+
171
+ if (this.api.mock) {
172
+ const count = MOCK_CONVERSATIONS.reduce(
173
+ (sum, c) => sum + (c.unread || 0),
174
+ 0
175
+ );
176
+ return {
177
+ status: true,
178
+ data: { unread_count: count, unread_conversations: count > 0 ? 1 : 0 },
179
+ };
180
+ }
181
+
182
+ return this.api._makeRequest('/widget/messenger/unread', {
183
+ method: 'GET',
184
+ headers: { Authorization: `Bearer ${this.api.sessionToken}` },
185
+ });
186
+ }
187
+
188
+ async markConversationAsRead(conversationId) {
189
+ await this.api._ensureSession();
190
+
191
+ if (this.api.mock) {
192
+ return { status: true };
193
+ }
194
+
195
+ return this.api._makeRequest(
196
+ `/widget/messenger/conversations/${conversationId}/read`,
197
+ {
198
+ method: 'POST',
199
+ headers: { Authorization: `Bearer ${this.api.sessionToken}` },
200
+ }
201
+ );
202
+ }
203
+
204
+ async getMessages(conversationId, options = {}) {
205
+ await this.api._ensureSession();
206
+
207
+ if (this.api.mock) {
208
+ await delay(200);
209
+ return {
210
+ status: true,
211
+ data: MOCK_MESSAGES[conversationId] || [],
212
+ meta: { total: 0, page: 1, limit: 50 },
213
+ };
214
+ }
215
+
216
+ const params = { ...options };
217
+ const endpoint = this.api._getEndpointWithParams(
218
+ `/widget/messenger/conversations/${conversationId}/messages`,
219
+ params
220
+ );
221
+ return this.api._makeRequest(endpoint, {
222
+ method: 'GET',
223
+ headers: { Authorization: `Bearer ${this.api.sessionToken}` },
224
+ });
225
+ }
226
+
227
+ async identifyContact(data) {
228
+ await this.api._ensureSession();
229
+
230
+ if (this.api.mock) {
231
+ await delay(300);
232
+ return {
233
+ status: true,
234
+ data: {
235
+ contact_id: 'mock_contact_' + Date.now(),
236
+ email: data.email,
237
+ name: data.name || '',
238
+ is_new: true,
239
+ },
240
+ };
241
+ }
242
+
243
+ return this.api._makeRequest('/widget/messenger/identify', {
244
+ method: 'POST',
245
+ headers: {
246
+ 'Content-Type': 'application/json',
247
+ Authorization: `Bearer ${this.api.sessionToken}`,
248
+ },
249
+ body: JSON.stringify({
250
+ email: data.email,
251
+ name: data.name || '',
252
+ phone: data.phone || '',
253
+ company: data.company || '',
254
+ avatar_url: data.avatar_url || '',
255
+ metadata: data.metadata || {},
256
+ }),
257
+ });
258
+ }
259
+
260
+ async sendTypingIndicator(conversationId, isTyping) {
261
+ await this.api._ensureSession();
262
+
263
+ if (this.api.mock) {
264
+ return { status: true };
265
+ }
266
+
267
+ return this.api._makeRequest(
268
+ `/widget/messenger/conversations/${conversationId}/typing`,
269
+ {
270
+ method: 'POST',
271
+ headers: {
272
+ 'Content-Type': 'application/json',
273
+ Authorization: `Bearer ${this.api.sessionToken}`,
274
+ },
275
+ body: JSON.stringify({ is_typing: isTyping }),
276
+ }
277
+ );
278
+ }
279
+ }
@@ -0,0 +1,127 @@
1
+ import { APIError } from '../../utils/errors.js';
2
+ import { MOCK_SURVEYS } from '../mock-data/index.js';
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 respondent = this._getRespondentContext(context);
19
+
20
+ const params = {
21
+ url:
22
+ context.url ||
23
+ (typeof window !== 'undefined' ? window.location.href : ''),
24
+ ...getDeviceInfo(),
25
+ ...(respondent.respondent_id && {
26
+ respondent_id: respondent.respondent_id,
27
+ }),
28
+ ...(respondent.email && { email: respondent.email }),
29
+ ...(context.includeEligibility !== undefined && {
30
+ include_eligibility: context.includeEligibility,
31
+ }),
32
+ ...(context.userProperties && {
33
+ user_properties: context.userProperties,
34
+ }),
35
+ };
36
+
37
+ return this.api._handleAuthRetry(async () => {
38
+ const endpoint = this.api._getEndpointWithParams(
39
+ '/widget/surveys/active',
40
+ params
41
+ );
42
+ return await this.api._makeRequest(endpoint, {
43
+ method: 'GET',
44
+ headers: { Authorization: `Bearer ${this.api.sessionToken}` },
45
+ });
46
+ });
47
+ }
48
+
49
+ _getRespondentContext(context = {}) {
50
+ const metadata =
51
+ typeof this.api.getMetadata === 'function'
52
+ ? this.api.getMetadata() || {}
53
+ : {};
54
+
55
+ return {
56
+ respondent_id:
57
+ context.respondentId || context.userId || metadata.user_id || null,
58
+ email: context.email || metadata.email || null,
59
+ };
60
+ }
61
+
62
+ async submitSurveyResponse(surveyId, responseData) {
63
+ if (!surveyId) throw new APIError(400, 'Survey ID is required');
64
+
65
+ await this.api._ensureSession();
66
+
67
+ if (this.api.mock) {
68
+ await delay(300);
69
+ return {
70
+ success: true,
71
+ data: {
72
+ id: 'mock_response_' + Date.now(),
73
+ survey_id: surveyId,
74
+ ...responseData,
75
+ },
76
+ message: 'Survey response submitted successfully!',
77
+ };
78
+ }
79
+
80
+ const respondent = this._getRespondentContext(responseData);
81
+
82
+ const payload = {
83
+ rating: responseData.rating,
84
+ feedback: responseData.feedback || '',
85
+ answers: responseData.answers || {},
86
+ ...(respondent.respondent_id && {
87
+ respondent_id: respondent.respondent_id,
88
+ }),
89
+ ...(respondent.email && { email: respondent.email }),
90
+ };
91
+
92
+ return this.api._handleAuthRetry(async () => {
93
+ return await this.api._makeRequest(
94
+ `/widget/surveys/${surveyId}/responses`,
95
+ {
96
+ method: 'POST',
97
+ body: JSON.stringify(payload),
98
+ headers: {
99
+ 'Content-Type': 'application/json',
100
+ Authorization: `Bearer ${this.api.sessionToken}`,
101
+ },
102
+ }
103
+ );
104
+ });
105
+ }
106
+
107
+ async dismissSurvey(surveyId) {
108
+ if (!surveyId) throw new APIError(400, 'Survey ID is required');
109
+
110
+ await this.api._ensureSession();
111
+
112
+ if (this.api.mock) {
113
+ await delay(100);
114
+ return { success: true, message: 'Survey dismissed successfully' };
115
+ }
116
+
117
+ return this.api._handleAuthRetry(async () => {
118
+ return await this.api._makeRequest(
119
+ `/widget/surveys/${surveyId}/dismiss`,
120
+ {
121
+ method: 'POST',
122
+ headers: { Authorization: `Bearer ${this.api.sessionToken}` },
123
+ }
124
+ );
125
+ });
126
+ }
127
+ }
@@ -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
+ }