@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@product7/feedback-sdk",
3
- "version": "1.2.4",
3
+ "version": "1.2.6",
4
4
  "description": "JavaScript SDK for integrating Product7 feedback widgets into any website",
5
5
  "main": "dist/feedback-sdk.js",
6
6
  "module": "src/index.js",
@@ -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