@product7/feedback-sdk 1.6.0 → 1.6.1

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.
@@ -4,6 +4,9 @@ export class HelpView {
4
4
  this.options = options;
5
5
  this.element = null;
6
6
  this._unsubscribe = null;
7
+
8
+ // Configurable header colour — defaults to Product7 blue
9
+ this._headerBg = `linear-gradient(180deg, #f0f4ff 0%, #ffffff 100%)`;
7
10
  }
8
11
 
9
12
  render() {
@@ -28,18 +31,23 @@ export class HelpView {
28
31
  <div class="messenger-help-header">
29
32
  <div class="messenger-help-header-top">
30
33
  <h2>Help</h2>
31
- <button class="sdk-close-btn" aria-label="Close">
34
+ <button class="sdk-close-btn messenger-help-close-btn" aria-label="Close">
32
35
  <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 256 256">
33
36
  <path d="M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z"></path>
34
37
  </svg>
35
38
  </button>
36
39
  </div>
37
- <input
38
- type="text"
39
- class="messenger-help-search-input"
40
- placeholder="Search for help..."
41
- value="${searchQuery}"
42
- />
40
+ <div class="messenger-help-search-wrap">
41
+ <span class="messenger-help-search-icon">
42
+ <iconify-icon icon="ph:magnifying-glass-duotone" width="16" height="16"></iconify-icon>
43
+ </span>
44
+ <input
45
+ type="text"
46
+ class="messenger-help-search-input"
47
+ placeholder="Search for help..."
48
+ value="${searchQuery}"
49
+ />
50
+ </div>
43
51
  </div>
44
52
 
45
53
  <div class="messenger-help-body">
@@ -66,13 +74,6 @@ export class HelpView {
66
74
  )
67
75
  : collections;
68
76
 
69
- const headerEl = this.element.querySelector(
70
- '.messenger-help-collections-header'
71
- );
72
- if (headerEl) {
73
- headerEl.textContent = `${filteredCollections.length} collections`;
74
- }
75
-
76
77
  if (filteredCollections.length === 0) {
77
78
  collectionsContainer.innerHTML = this._renderEmptyState();
78
79
  return;
@@ -85,32 +86,125 @@ export class HelpView {
85
86
  this._attachCollectionEvents();
86
87
  }
87
88
 
89
+ // ─── Avatar helpers ──────────────────────────────────────────────────────────
90
+
91
+ _avatarColors = [
92
+ { bg: '#EF4444', text: '#FFFFFF' },
93
+ { bg: '#F97316', text: '#FFFFFF' },
94
+ { bg: '#F59E0B', text: '#FFFFFF' },
95
+ { bg: '#10B981', text: '#FFFFFF' },
96
+ { bg: '#06B6D4', text: '#FFFFFF' },
97
+ { bg: '#3B82F6', text: '#FFFFFF' },
98
+ { bg: '#8B5CF6', text: '#FFFFFF' },
99
+ { bg: '#EC4899', text: '#FFFFFF' },
100
+ ];
101
+
102
+ _getAvatarColor(id) {
103
+ const hash = id
104
+ .split('')
105
+ .reduce((acc, char) => acc + char.charCodeAt(0), 0);
106
+ return this._avatarColors[hash % this._avatarColors.length];
107
+ }
108
+
109
+ _getInitials(name) {
110
+ if (!name) return 'A';
111
+ return name
112
+ .split(' ')
113
+ .map((n) => n[0])
114
+ .join('')
115
+ .toUpperCase()
116
+ .slice(0, 2);
117
+ }
118
+
119
+ _renderAuthorAvatar(collection) {
120
+ if (collection.author?.picture) {
121
+ return `
122
+ <img
123
+ src="${collection.author.picture}"
124
+ alt="${collection.author.name || ''}"
125
+ class="messenger-help-collection-avatar"
126
+ title="${collection.author.name || ''}"
127
+ />
128
+ `;
129
+ }
130
+
131
+ const { bg, text } = this._getAvatarColor(collection.id);
132
+ const initials = collection.author?.name
133
+ ? this._getInitials(collection.author.name)
134
+ : 'A';
135
+
136
+ return `
137
+ <span
138
+ class="messenger-help-collection-avatar messenger-help-collection-avatar--initials"
139
+ style="background-color: ${bg}; color: ${text};"
140
+ title="${collection.author?.name || 'Author'}"
141
+ >${initials}</span>
142
+ `;
143
+ }
144
+
145
+ // ─── Icon resolution ─────────────────────────────────────────────────────────
146
+
147
+ _resolveCollectionIcon(icon) {
148
+ if (!icon) return this._defaultCollectionIcon();
149
+
150
+ if (icon.trimStart().startsWith('<')) {
151
+ return `<span class="messenger-help-collection-icon">${icon}</span>`;
152
+ }
153
+
154
+ if (icon.startsWith('ph:')) {
155
+ return `
156
+ <span class="messenger-help-collection-icon">
157
+ <iconify-icon icon="${icon}" width="20" height="20"></iconify-icon>
158
+ </span>
159
+ `;
160
+ }
161
+
162
+ return this._defaultCollectionIcon();
163
+ }
164
+
165
+ _defaultCollectionIcon() {
166
+ return `
167
+ <span class="messenger-help-collection-icon">
168
+ <iconify-icon icon="ph:book-open-duotone" width="20" height="20"></iconify-icon>
169
+ </span>
170
+ `;
171
+ }
172
+
173
+ // ─── Rendering ───────────────────────────────────────────────────────────────
174
+
88
175
  _renderCollectionItem(collection) {
89
176
  const articleCount = collection.articleCount || 0;
177
+ const iconHtml = this._resolveCollectionIcon(collection.icon);
178
+ const avatarHtml = this._renderAuthorAvatar(collection);
179
+
90
180
  return `
91
181
  <div class="messenger-help-collection" data-collection-id="${collection.id}">
182
+ ${iconHtml}
92
183
  <div class="messenger-help-collection-content">
93
184
  <h3 class="messenger-help-collection-title">${collection.title}</h3>
94
- <p class="messenger-help-collection-desc">${collection.description || ''}</p>
95
- <span class="messenger-help-collection-count">${articleCount} articles</span>
185
+ ${collection.description ? `<p class="messenger-help-collection-desc">${collection.description}</p>` : ''}
186
+ <div class="messenger-help-collection-meta">
187
+ ${avatarHtml}
188
+ <span class="messenger-help-collection-count">
189
+ ${articleCount} ${articleCount === 1 ? 'article' : 'articles'}
190
+ </span>
191
+ </div>
96
192
  </div>
97
- <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="#000000" viewBox="0 0 256 256" class="messenger-help-collection-arrow">
193
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 256 256" class="messenger-help-collection-arrow">
98
194
  <path d="M181.66,133.66l-80,80a8,8,0,0,1-11.32-11.32L164.69,128,90.34,53.66a8,8,0,0,1,11.32-11.32l80,80A8,8,0,0,1,181.66,133.66Z"></path>
99
195
  </svg>
100
196
  </div>
101
197
  `;
102
198
  }
103
199
 
104
- _renderEmptyState() {
105
- const isSearching = this.state.helpSearchQuery;
200
+ // ─── Empty states ────────────────────────────────────────────────────────────
106
201
 
107
- if (isSearching) {
202
+ _renderEmptyState() {
203
+ if (this.state.helpSearchQuery) {
108
204
  return `
109
205
  <div class="messenger-empty-state">
110
206
  <div class="messenger-empty-state-icon">
111
- <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" fill="currentColor" viewBox="0 0 256 256">
112
- <path d="M229.66,218.34l-50.07-50.06a88.11,88.11,0,1,0-11.31,11.31l50.06,50.07a8,8,0,0,0,11.32-11.32ZM40,112a72,72,0,1,1,72,72A72.08,72.08,0,0,1,40,112Z"></path>
113
- </svg>
207
+ <iconify-icon icon="ph:magnifying-glass-duotone" width="48" height="48"></iconify-icon>
114
208
  </div>
115
209
  <h3>No results found</h3>
116
210
  <p>Try a different search term</p>
@@ -121,9 +215,7 @@ export class HelpView {
121
215
  return `
122
216
  <div class="messenger-empty-state">
123
217
  <div class="messenger-empty-state-icon">
124
- <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" fill="currentColor" viewBox="0 0 256 256">
125
- <path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm16-40a8,8,0,0,1-8,8,16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40A8,8,0,0,1,144,176ZM112,84a12,12,0,1,1,12,12A12,12,0,0,1,112,84Z"></path>
126
- </svg>
218
+ <iconify-icon icon="ph:books-duotone" width="48" height="48"></iconify-icon>
127
219
  </div>
128
220
  <h3>Help collections</h3>
129
221
  <p>No collections available yet</p>
@@ -131,9 +223,11 @@ export class HelpView {
131
223
  `;
132
224
  }
133
225
 
226
+ // ─── Events ──────────────────────────────────────────────────────────────────
227
+
134
228
  _attachEvents() {
135
229
  this.element
136
- .querySelector('.sdk-close-btn')
230
+ .querySelector('.messenger-help-close-btn')
137
231
  .addEventListener('click', () => {
138
232
  this.state.setOpen(false);
139
233
  });
@@ -161,7 +255,7 @@ export class HelpView {
161
255
  const collection = this.state.helpArticles.find(
162
256
  (c) => c.id === collectionId
163
257
  );
164
- if (collection && collection.url) {
258
+ if (collection?.url) {
165
259
  window.open(collection.url, '_blank');
166
260
  } else if (this.options.onArticleClick) {
167
261
  this.options.onArticleClick(collection);
@@ -170,6 +264,8 @@ export class HelpView {
170
264
  });
171
265
  }
172
266
 
267
+ // ─── Lifecycle ───────────────────────────────────────────────────────────────
268
+
173
269
  destroy() {
174
270
  if (this._unsubscribe) {
175
271
  this._unsubscribe();