@product7/product7-js 0.3.5 → 0.3.7

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.
@@ -54,7 +54,7 @@
54
54
  },
55
55
  ];
56
56
 
57
- const MOCK_CONVERSATIONS$1 = [
57
+ const MOCK_CONVERSATIONS = [
58
58
  {
59
59
  id: 'conv_1',
60
60
  subject: 'Question about pricing',
@@ -85,7 +85,7 @@
85
85
  },
86
86
  ];
87
87
 
88
- const MOCK_MESSAGES$1 = {
88
+ const MOCK_MESSAGES = {
89
89
  conv_1: [
90
90
  {
91
91
  id: 'msg_1',
@@ -443,12 +443,19 @@
443
443
  };
444
444
  }
445
445
 
446
+ const contact = this.api.getContactIdentity();
447
+
446
448
  const payload = {
447
449
  board:
448
450
  feedbackData.board_id || feedbackData.board || feedbackData.boardName,
449
451
  title: feedbackData.title,
450
452
  content: feedbackData.content,
451
453
  attachments: feedbackData.attachments || [],
454
+ ...(contact?.contactId && {
455
+ contact_id: contact.contactId,
456
+ contact_email: contact.contactEmail,
457
+ contact_name: contact.contactName,
458
+ }),
452
459
  };
453
460
 
454
461
  return this.api._handleAuthRetry(async () => {
@@ -549,6 +556,10 @@
549
556
  agents_online: true,
550
557
  online_count: 2,
551
558
  response_time: 'Usually replies within a few minutes',
559
+ available_agents: [
560
+ { full_name: 'Sarah', picture: '' },
561
+ { full_name: 'Tom', picture: '' },
562
+ ],
552
563
  },
553
564
  };
554
565
  }
@@ -566,8 +577,8 @@
566
577
  await delay$1(300);
567
578
  return {
568
579
  status: true,
569
- data: MOCK_CONVERSATIONS$1,
570
- meta: { total: MOCK_CONVERSATIONS$1.length, page: 1, limit: 20 },
580
+ data: MOCK_CONVERSATIONS,
581
+ meta: { total: MOCK_CONVERSATIONS.length, page: 1, limit: 20 },
571
582
  };
572
583
  }
573
584
 
@@ -586,10 +597,10 @@
586
597
 
587
598
  if (this.api.mock) {
588
599
  await delay$1(200);
589
- const conv = MOCK_CONVERSATIONS$1.find((c) => c.id === conversationId);
600
+ const conv = MOCK_CONVERSATIONS.find((c) => c.id === conversationId);
590
601
  return {
591
602
  status: true,
592
- data: { ...conv, messages: MOCK_MESSAGES$1[conversationId] || [] },
603
+ data: { ...conv, messages: MOCK_MESSAGES[conversationId] || [] },
593
604
  };
594
605
  }
595
606
 
@@ -623,8 +634,8 @@
623
634
  },
624
635
  ],
625
636
  };
626
- MOCK_CONVERSATIONS$1.unshift(newConv);
627
- MOCK_MESSAGES$1[newConv.id] = newConv.messages;
637
+ MOCK_CONVERSATIONS.unshift(newConv);
638
+ MOCK_MESSAGES[newConv.id] = newConv.messages;
628
639
  return { status: true, data: newConv };
629
640
  }
630
641
 
@@ -654,10 +665,10 @@
654
665
  created_at: new Date().toISOString(),
655
666
  attachments: data.attachments || [],
656
667
  };
657
- if (!MOCK_MESSAGES$1[conversationId]) {
658
- MOCK_MESSAGES$1[conversationId] = [];
668
+ if (!MOCK_MESSAGES[conversationId]) {
669
+ MOCK_MESSAGES[conversationId] = [];
659
670
  }
660
- MOCK_MESSAGES$1[conversationId].push(newMessage);
671
+ MOCK_MESSAGES[conversationId].push(newMessage);
661
672
  return { status: true, data: newMessage };
662
673
  }
663
674
 
@@ -681,7 +692,7 @@
681
692
  await this.api._ensureSession();
682
693
 
683
694
  if (this.api.mock) {
684
- const count = MOCK_CONVERSATIONS$1.reduce(
695
+ const count = MOCK_CONVERSATIONS.reduce(
685
696
  (sum, c) => sum + (c.unread || 0),
686
697
  0
687
698
  );
@@ -720,7 +731,7 @@
720
731
  await delay$1(200);
721
732
  return {
722
733
  status: true,
723
- data: MOCK_MESSAGES$1[conversationId] || [],
734
+ data: MOCK_MESSAGES[conversationId] || [],
724
735
  meta: { total: 0, page: 1, limit: 50 },
725
736
  };
726
737
  }
@@ -736,10 +747,48 @@
736
747
  });
737
748
  }
738
749
 
750
+ async uploadFile(base64Data, filename) {
751
+ await this.api._ensureSession();
752
+
753
+ if (this.api.mock) {
754
+ await delay$1(300);
755
+ return { status: true, url: `https://mock-cdn.example.com/${filename}` };
756
+ }
757
+
758
+ return this.api._makeRequest('/widget/messenger/upload', {
759
+ method: 'POST',
760
+ headers: {
761
+ 'Content-Type': 'application/json',
762
+ Authorization: `Bearer ${this.api.sessionToken}`,
763
+ },
764
+ body: JSON.stringify({ file: base64Data, filename }),
765
+ });
766
+ }
767
+
739
768
  async identifyContact(data) {
740
769
  return this.api.identify(data);
741
770
  }
742
771
 
772
+ async submitRating(conversationId, data) {
773
+ await this.api._ensureSession();
774
+
775
+ if (this.api.mock) {
776
+ return { status: true, data: { rated: true } };
777
+ }
778
+
779
+ return this.api._makeRequest(
780
+ `/widget/messenger/conversations/${conversationId}/rating`,
781
+ {
782
+ method: 'POST',
783
+ headers: {
784
+ 'Content-Type': 'application/json',
785
+ Authorization: `Bearer ${this.api.sessionToken}`,
786
+ },
787
+ body: JSON.stringify(data),
788
+ }
789
+ );
790
+ }
791
+
743
792
  async sendTypingIndicator(conversationId, isTyping) {
744
793
  await this.api._ensureSession();
745
794
 
@@ -773,7 +822,7 @@
773
822
  }
774
823
  }
775
824
 
776
- let APIError$1 = class APIError extends Error {
825
+ class APIError extends Error {
777
826
  constructor(status, message, response) {
778
827
  super(message);
779
828
  this.name = 'APIError';
@@ -796,7 +845,7 @@
796
845
  isServerError() {
797
846
  return this.status >= 500 && this.status < 600;
798
847
  }
799
- };
848
+ }
800
849
 
801
850
  class WidgetError extends Error {
802
851
  constructor(message, widgetType, widgetId) {
@@ -894,7 +943,7 @@
894
943
  }
895
944
 
896
945
  async submitSurveyResponse(surveyId, responseData) {
897
- if (!surveyId) throw new APIError$1(400, 'Survey ID is required');
946
+ if (!surveyId) throw new APIError(400, 'Survey ID is required');
898
947
 
899
948
  await this.api._ensureSession();
900
949
 
@@ -912,6 +961,7 @@
912
961
  }
913
962
 
914
963
  const respondent = this._getRespondentContext(responseData);
964
+ const contact = this.api.getContactIdentity?.() || null;
915
965
 
916
966
  const payload = {
917
967
  rating: responseData.rating,
@@ -921,6 +971,11 @@
921
971
  respondent_id: respondent.respondent_id,
922
972
  }),
923
973
  ...(respondent.email && { email: respondent.email }),
974
+ ...(contact?.contactId && {
975
+ contact_id: contact.contactId,
976
+ contact_email: contact.contactEmail,
977
+ contact_name: contact.contactName,
978
+ }),
924
979
  };
925
980
 
926
981
  return this.api._handleAuthRetry(async () => {
@@ -939,7 +994,7 @@
939
994
  }
940
995
 
941
996
  async dismissSurvey(surveyId) {
942
- if (!surveyId) throw new APIError$1(400, 'Survey ID is required');
997
+ if (!surveyId) throw new APIError(400, 'Survey ID is required');
943
998
 
944
999
  await this.api._ensureSession();
945
1000
 
@@ -1025,7 +1080,7 @@
1025
1080
  }
1026
1081
 
1027
1082
  if (!this.workspace) {
1028
- throw new APIError$1(400, 'Missing workspace for initialization');
1083
+ throw new APIError(400, 'Missing workspace for initialization');
1029
1084
  }
1030
1085
 
1031
1086
  if (this.mock) {
@@ -1081,7 +1136,7 @@
1081
1136
  configVersion: initData.configVersion,
1082
1137
  };
1083
1138
  } catch (error) {
1084
- throw new APIError$1(
1139
+ throw new APIError(
1085
1140
  error.status || 500,
1086
1141
  `Failed to initialize widget: ${error.message}`,
1087
1142
  error.response
@@ -1099,11 +1154,11 @@
1099
1154
  const expiresIn = Number(payload.expires_in ?? payload.expiresIn);
1100
1155
 
1101
1156
  if (!sessionToken) {
1102
- throw new APIError$1(500, 'Invalid init response: missing session_token');
1157
+ throw new APIError(500, 'Invalid init response: missing session_token');
1103
1158
  }
1104
1159
 
1105
1160
  if (!Number.isFinite(expiresIn) || expiresIn <= 0) {
1106
- throw new APIError$1(500, 'Invalid init response: missing expires_in');
1161
+ throw new APIError(500, 'Invalid init response: missing expires_in');
1107
1162
  }
1108
1163
 
1109
1164
  return {
@@ -1124,7 +1179,7 @@
1124
1179
  await this.init();
1125
1180
  }
1126
1181
  if (!this.sessionToken) {
1127
- throw new APIError$1(401, 'No valid session token available');
1182
+ throw new APIError(401, 'No valid session token available');
1128
1183
  }
1129
1184
  }
1130
1185
 
@@ -1149,7 +1204,7 @@
1149
1204
  }
1150
1205
 
1151
1206
  if (!this.metadata) {
1152
- throw new APIError$1(400, 'Missing user context for identify');
1207
+ throw new APIError(400, 'Missing user context for identify');
1153
1208
  }
1154
1209
 
1155
1210
  await this._ensureSession();
@@ -1235,6 +1290,15 @@
1235
1290
  if (!stored) return false;
1236
1291
 
1237
1292
  const sessionData = JSON.parse(stored);
1293
+
1294
+ // Invalidate mock tokens when not in mock mode (and vice versa)
1295
+ const isMockToken =
1296
+ sessionData.token && sessionData.token.startsWith('mock_');
1297
+ if (isMockToken !== this.mock) {
1298
+ localStorage.removeItem('product7_session');
1299
+ return false;
1300
+ }
1301
+
1238
1302
  if (
1239
1303
  this.workspace &&
1240
1304
  sessionData.workspace &&
@@ -1243,6 +1307,7 @@
1243
1307
  localStorage.removeItem('product7_session');
1244
1308
  return false;
1245
1309
  }
1310
+
1246
1311
  this.sessionToken = sessionData.token;
1247
1312
  this.sessionExpiry = new Date(sessionData.expiry);
1248
1313
 
@@ -1308,7 +1373,7 @@
1308
1373
  errorMessage = (await response.text()) || errorMessage;
1309
1374
  }
1310
1375
 
1311
- throw new APIError$1(response.status, errorMessage, responseData);
1376
+ throw new APIError(response.status, errorMessage, responseData);
1312
1377
  }
1313
1378
 
1314
1379
  const contentType = response.headers.get('content-type');
@@ -1318,8 +1383,8 @@
1318
1383
 
1319
1384
  return await response.text();
1320
1385
  } catch (error) {
1321
- if (error instanceof APIError$1) throw error;
1322
- throw new APIError$1(0, error.message, null);
1386
+ if (error instanceof APIError) throw error;
1387
+ throw new APIError(0, error.message, null);
1323
1388
  }
1324
1389
  }
1325
1390
 
@@ -1404,29 +1469,7 @@
1404
1469
  }
1405
1470
 
1406
1471
  async checkAgentsOnline() {
1407
- if (!this.isSessionValid()) {
1408
- await this.init();
1409
- }
1410
-
1411
- if (this.mock) {
1412
- return {
1413
- status: true,
1414
- data: {
1415
- agents_online: true,
1416
- online_count: 2,
1417
- response_time: 'Usually replies within a few minutes',
1418
- available_agents: [
1419
- { full_name: 'Sarah', picture: '' },
1420
- { full_name: 'Tom', picture: '' },
1421
- ],
1422
- },
1423
- };
1424
- }
1425
-
1426
- return this._makeRequest('/widget/messenger/agents/online', {
1427
- method: 'GET',
1428
- headers: { Authorization: `Bearer ${this.sessionToken}` },
1429
- });
1472
+ return this.messenger.checkAgentsOnline();
1430
1473
  }
1431
1474
 
1432
1475
  async getConversations(options) {
@@ -1442,137 +1485,15 @@
1442
1485
  }
1443
1486
 
1444
1487
  async startConversation(data) {
1445
- if (!this.isSessionValid()) {
1446
- console.log(
1447
- '[APIService] startConversation: session invalid, calling init...'
1448
- );
1449
- try {
1450
- await this.init();
1451
- console.log(
1452
- '[APIService] startConversation: init result, token:',
1453
- this.sessionToken ? 'set' : 'null'
1454
- );
1455
- } catch (initError) {
1456
- console.error(
1457
- '[APIService] startConversation: init failed:',
1458
- initError.message
1459
- );
1460
- throw initError;
1461
- }
1462
- }
1463
-
1464
- if (!this.sessionToken) {
1465
- console.error(
1466
- '[APIService] startConversation: no session token after init'
1467
- );
1468
- throw new APIError(401, 'No valid session token available');
1469
- }
1470
-
1471
- console.log(
1472
- '[APIService] startConversation: sending to',
1473
- `${this.baseURL}/widget/messenger/conversations`,
1474
- 'mock:',
1475
- this.mock
1476
- );
1477
-
1478
- if (this.mock) {
1479
- await new Promise((resolve) => setTimeout(resolve, 300));
1480
- const newConv = {
1481
- id: 'conv_' + Date.now(),
1482
- subject: data.subject || 'New conversation',
1483
- status: 'open',
1484
- last_message_at: new Date().toISOString(),
1485
- created_at: new Date().toISOString(),
1486
- messages: [
1487
- {
1488
- id: 'msg_' + Date.now(),
1489
- content: data.message,
1490
- sender_type: 'customer',
1491
- created_at: new Date().toISOString(),
1492
- },
1493
- ],
1494
- };
1495
- MOCK_CONVERSATIONS.unshift(newConv);
1496
- MOCK_MESSAGES[newConv.id] = newConv.messages;
1497
- return { status: true, data: newConv };
1498
- }
1499
-
1500
- return this._makeRequest('/widget/messenger/conversations', {
1501
- method: 'POST',
1502
- headers: {
1503
- 'Content-Type': 'application/json',
1504
- Authorization: `Bearer ${this.sessionToken}`,
1505
- },
1506
- body: JSON.stringify({
1507
- message: data.message,
1508
- subject: data.subject || '',
1509
- }),
1510
- });
1488
+ return this.messenger.startConversation(data);
1511
1489
  }
1512
1490
 
1513
1491
  async sendMessage(conversationId, data) {
1514
- if (!this.isSessionValid()) {
1515
- await this.init();
1516
- }
1517
-
1518
- if (this.mock) {
1519
- await new Promise((resolve) => setTimeout(resolve, 200));
1520
- const newMessage = {
1521
- id: 'msg_' + Date.now(),
1522
- content: data.content,
1523
- attachments: data.attachments || [],
1524
- sender_type: 'customer',
1525
- created_at: new Date().toISOString(),
1526
- };
1527
- if (!MOCK_MESSAGES[conversationId]) {
1528
- MOCK_MESSAGES[conversationId] = [];
1529
- }
1530
- MOCK_MESSAGES[conversationId].push(newMessage);
1531
- return { status: true, data: newMessage };
1532
- }
1533
-
1534
- const payload = { content: data.content };
1535
- if (data.attachments && data.attachments.length > 0) {
1536
- payload.attachments = data.attachments;
1537
- }
1538
-
1539
- return this._makeRequest(
1540
- `/widget/messenger/conversations/${conversationId}/messages`,
1541
- {
1542
- method: 'POST',
1543
- headers: {
1544
- 'Content-Type': 'application/json',
1545
- Authorization: `Bearer ${this.sessionToken}`,
1546
- },
1547
- body: JSON.stringify(payload),
1548
- }
1549
- );
1492
+ return this.messenger.sendMessage(conversationId, data);
1550
1493
  }
1551
1494
 
1552
- /**
1553
- * Upload a file to CDN via widget endpoint
1554
- * @param {string} base64Data - Base64 encoded file data (with or without data URI prefix)
1555
- * @param {string} filename - Original filename
1556
- * @returns {Promise<Object>} Response with url
1557
- */
1558
1495
  async uploadFile(base64Data, filename) {
1559
- if (!this.isSessionValid()) {
1560
- await this.init();
1561
- }
1562
-
1563
- if (this.mock) {
1564
- await new Promise((resolve) => setTimeout(resolve, 300));
1565
- return { status: true, url: `https://mock-cdn.example.com/${filename}` };
1566
- }
1567
-
1568
- return this._makeRequest('/widget/messenger/upload', {
1569
- method: 'POST',
1570
- headers: {
1571
- 'Content-Type': 'application/json',
1572
- Authorization: `Bearer ${this.sessionToken}`,
1573
- },
1574
- body: JSON.stringify({ file: base64Data, filename }),
1575
- });
1496
+ return this.messenger.uploadFile(base64Data, filename);
1576
1497
  }
1577
1498
 
1578
1499
  async sendTypingIndicator(conversationId, isTyping) {
@@ -1609,7 +1530,7 @@
1609
1530
  return mockResponse;
1610
1531
  }
1611
1532
 
1612
- const response = await this._makeRequest('/widget/messenger/identify', {
1533
+ const response = await this._makeRequest('/widget/identify', {
1613
1534
  method: 'POST',
1614
1535
  headers: {
1615
1536
  'Content-Type': 'application/json',
@@ -1692,67 +1613,6 @@
1692
1613
  async getChangelogs(options) {
1693
1614
  return this.changelog.getChangelogs(options);
1694
1615
  }
1695
-
1696
- _loadStoredSession() {
1697
- if (typeof localStorage === 'undefined') return false;
1698
-
1699
- try {
1700
- const stored = localStorage.getItem('product7_session');
1701
- if (!stored) return false;
1702
-
1703
- const sessionData = JSON.parse(stored);
1704
-
1705
- // Invalidate mock tokens when not in mock mode (and vice versa)
1706
- const isMockToken =
1707
- sessionData.token && sessionData.token.startsWith('mock_');
1708
- if (isMockToken !== this.mock) {
1709
- localStorage.removeItem('product7_session');
1710
- return false;
1711
- }
1712
-
1713
- this.sessionToken = sessionData.token;
1714
- this.sessionExpiry = new Date(sessionData.expiry);
1715
-
1716
- return this.isSessionValid();
1717
- } catch (error) {
1718
- return false;
1719
- }
1720
- }
1721
-
1722
- async _makeRequest(endpoint, options = {}) {
1723
- const url = `${this.baseURL}${endpoint}`;
1724
-
1725
- try {
1726
- const response = await fetch(url, options);
1727
-
1728
- if (!response.ok) {
1729
- let errorMessage = `HTTP ${response.status}`;
1730
- let responseData = null;
1731
-
1732
- try {
1733
- responseData = await response.json();
1734
- errorMessage =
1735
- responseData.message || responseData.error || errorMessage;
1736
- } catch (e) {
1737
- errorMessage = (await response.text()) || errorMessage;
1738
- }
1739
-
1740
- throw new APIError(response.status, errorMessage, responseData);
1741
- }
1742
-
1743
- const contentType = response.headers.get('content-type');
1744
- if (contentType && contentType.includes('application/json')) {
1745
- return await response.json();
1746
- }
1747
-
1748
- return await response.text();
1749
- } catch (error) {
1750
- if (error instanceof APIError) {
1751
- throw error;
1752
- }
1753
- throw new APIError(0, error.message, null);
1754
- }
1755
- }
1756
1616
  }
1757
1617
 
1758
1618
  class EventBus {
@@ -15774,7 +15634,7 @@
15774
15634
  EventBus,
15775
15635
  APIService,
15776
15636
  SDKError,
15777
- APIError: APIError$1,
15637
+ APIError,
15778
15638
  WidgetError,
15779
15639
  ConfigError,
15780
15640
  ValidationError,
@@ -15793,7 +15653,7 @@
15793
15653
  }
15794
15654
  }
15795
15655
 
15796
- exports.APIError = APIError$1;
15656
+ exports.APIError = APIError;
15797
15657
  exports.APIService = APIService;
15798
15658
  exports.BaseWidget = BaseWidget;
15799
15659
  exports.ButtonWidget = ButtonWidget;