@product7/feedback-sdk 1.2.8 → 1.3.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.
- package/dist/feedback-sdk.js +3646 -2406
- 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 +2 -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 +86 -1002
- package/src/core/BaseAPIService.js +245 -0
- package/src/core/FeedbackSDK.js +3 -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/messengerStyles.js +580 -9
- package/src/styles/styles.js +5 -1379
- package/src/widgets/MessengerWidget.js +204 -21
- package/src/widgets/messenger/MessengerState.js +31 -1
- 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 +356 -49
- package/src/widgets/messenger/views/ConversationsView.js +29 -18
- package/src/widgets/messenger/views/HelpView.js +15 -14
- package/src/widgets/messenger/views/HomeView.js +53 -22
- package/src/widgets/messenger/views/PreChatFormView.js +224 -0
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HelpView - Help collections browse with Intercom-style design
|
|
3
|
-
*/
|
|
4
1
|
export class HelpView {
|
|
5
2
|
constructor(state, options = {}) {
|
|
6
3
|
this.state = state;
|
|
@@ -15,7 +12,6 @@ export class HelpView {
|
|
|
15
12
|
|
|
16
13
|
this._updateContent();
|
|
17
14
|
|
|
18
|
-
// Subscribe to state changes
|
|
19
15
|
this._unsubscribe = this.state.subscribe((type) => {
|
|
20
16
|
if (type === 'helpArticlesUpdate' || type === 'helpSearchChange') {
|
|
21
17
|
this._updateCollectionsList();
|
|
@@ -34,7 +30,9 @@ export class HelpView {
|
|
|
34
30
|
<div class="messenger-help-header">
|
|
35
31
|
<h2>Help</h2>
|
|
36
32
|
<button class="messenger-close-btn" aria-label="Close">
|
|
37
|
-
<
|
|
33
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="#000000" viewBox="0 0 256 256">
|
|
34
|
+
<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>
|
|
35
|
+
</svg>
|
|
38
36
|
</button>
|
|
39
37
|
</div>
|
|
40
38
|
|
|
@@ -46,7 +44,9 @@ export class HelpView {
|
|
|
46
44
|
placeholder="Search for help"
|
|
47
45
|
value="${searchQuery}"
|
|
48
46
|
/>
|
|
49
|
-
<
|
|
47
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="#000000" viewBox="0 0 256 256" class="messenger-help-search-icon">
|
|
48
|
+
<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>
|
|
49
|
+
</svg>
|
|
50
50
|
</div>
|
|
51
51
|
</div>
|
|
52
52
|
|
|
@@ -69,7 +69,6 @@ export class HelpView {
|
|
|
69
69
|
const collections = this.state.helpArticles || [];
|
|
70
70
|
const searchQuery = (this.state.helpSearchQuery || '').toLowerCase();
|
|
71
71
|
|
|
72
|
-
// Filter collections by search
|
|
73
72
|
const filteredCollections = searchQuery
|
|
74
73
|
? collections.filter(
|
|
75
74
|
(c) =>
|
|
@@ -78,7 +77,6 @@ export class HelpView {
|
|
|
78
77
|
)
|
|
79
78
|
: collections;
|
|
80
79
|
|
|
81
|
-
// Update collection count
|
|
82
80
|
const headerEl = this.element.querySelector(
|
|
83
81
|
'.messenger-help-collections-header'
|
|
84
82
|
);
|
|
@@ -95,7 +93,6 @@ export class HelpView {
|
|
|
95
93
|
.map((collection) => this._renderCollectionItem(collection))
|
|
96
94
|
.join('');
|
|
97
95
|
|
|
98
|
-
// Attach click events
|
|
99
96
|
this._attachCollectionEvents();
|
|
100
97
|
}
|
|
101
98
|
|
|
@@ -108,7 +105,9 @@ export class HelpView {
|
|
|
108
105
|
<p class="messenger-help-collection-desc">${collection.description || ''}</p>
|
|
109
106
|
<span class="messenger-help-collection-count">${articleCount} articles</span>
|
|
110
107
|
</div>
|
|
111
|
-
<
|
|
108
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="#000000" viewBox="0 0 256 256" class="messenger-help-collection-arrow">
|
|
109
|
+
<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>
|
|
110
|
+
</svg>
|
|
112
111
|
</div>
|
|
113
112
|
`;
|
|
114
113
|
}
|
|
@@ -120,7 +119,9 @@ export class HelpView {
|
|
|
120
119
|
return `
|
|
121
120
|
<div class="messenger-help-empty">
|
|
122
121
|
<div class="messenger-help-empty-icon">
|
|
123
|
-
<
|
|
122
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" fill="#000000" viewBox="0 0 256 256">
|
|
123
|
+
<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>
|
|
124
|
+
</svg>
|
|
124
125
|
</div>
|
|
125
126
|
<h3>No results found</h3>
|
|
126
127
|
<p>Try a different search term</p>
|
|
@@ -131,7 +132,9 @@ export class HelpView {
|
|
|
131
132
|
return `
|
|
132
133
|
<div class="messenger-help-empty">
|
|
133
134
|
<div class="messenger-help-empty-icon">
|
|
134
|
-
<
|
|
135
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" fill="#000000" viewBox="0 0 256 256">
|
|
136
|
+
<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>
|
|
137
|
+
</svg>
|
|
135
138
|
</div>
|
|
136
139
|
<h3>Help collections</h3>
|
|
137
140
|
<p>No collections available yet</p>
|
|
@@ -140,14 +143,12 @@ export class HelpView {
|
|
|
140
143
|
}
|
|
141
144
|
|
|
142
145
|
_attachEvents() {
|
|
143
|
-
// Close button
|
|
144
146
|
this.element
|
|
145
147
|
.querySelector('.messenger-close-btn')
|
|
146
148
|
.addEventListener('click', () => {
|
|
147
149
|
this.state.setOpen(false);
|
|
148
150
|
});
|
|
149
151
|
|
|
150
|
-
// Search input
|
|
151
152
|
const searchInput = this.element.querySelector(
|
|
152
153
|
'.messenger-help-search-input'
|
|
153
154
|
);
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HomeView - Welcome screen with team info and quick actions
|
|
3
|
-
*/
|
|
4
1
|
export class HomeView {
|
|
5
2
|
constructor(state, options = {}) {
|
|
6
3
|
this.state = state;
|
|
@@ -15,7 +12,6 @@ export class HomeView {
|
|
|
15
12
|
|
|
16
13
|
this._updateContent();
|
|
17
14
|
|
|
18
|
-
// Subscribe to state changes to re-render when data loads
|
|
19
15
|
this._unsubscribe = this.state.subscribe((type) => {
|
|
20
16
|
if (
|
|
21
17
|
type === 'homeChangelogUpdate' ||
|
|
@@ -41,7 +37,9 @@ export class HomeView {
|
|
|
41
37
|
</div>
|
|
42
38
|
<div class="messenger-home-avatars">${avatarsHtml}</div>
|
|
43
39
|
<button class="messenger-close-btn" aria-label="Close">
|
|
44
|
-
<
|
|
40
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="#000000" viewBox="0 0 256 256">
|
|
41
|
+
<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>
|
|
42
|
+
</svg>
|
|
45
43
|
</button>
|
|
46
44
|
</div>
|
|
47
45
|
<div class="messenger-home-welcome">
|
|
@@ -52,10 +50,7 @@ export class HomeView {
|
|
|
52
50
|
</div>
|
|
53
51
|
|
|
54
52
|
<div class="messenger-home-body">
|
|
55
|
-
|
|
56
|
-
<span>Send us a message</span>
|
|
57
|
-
<i class="ph ph-arrow-right" style="font-size: 16px;"></i>
|
|
58
|
-
</button>
|
|
53
|
+
${this._renderMessageButton()}
|
|
59
54
|
|
|
60
55
|
${this._renderFeaturedCard()}
|
|
61
56
|
|
|
@@ -69,7 +64,6 @@ export class HomeView {
|
|
|
69
64
|
_renderAvatarStack() {
|
|
70
65
|
const avatars = this.state.teamAvatars;
|
|
71
66
|
if (!avatars || avatars.length === 0) {
|
|
72
|
-
// Default avatars with initials
|
|
73
67
|
return `
|
|
74
68
|
<div class="messenger-avatar-stack">
|
|
75
69
|
<div class="messenger-avatar" style="background: #5856d6;">S</div>
|
|
@@ -118,8 +112,38 @@ export class HomeView {
|
|
|
118
112
|
`;
|
|
119
113
|
}
|
|
120
114
|
|
|
115
|
+
_renderMessageButton() {
|
|
116
|
+
const openConversation = this.state.conversations.find(
|
|
117
|
+
(c) => c.status === 'open'
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
if (openConversation) {
|
|
121
|
+
const preview = openConversation.lastMessage
|
|
122
|
+
? (openConversation.lastMessage.length > 40
|
|
123
|
+
? openConversation.lastMessage.substring(0, 40) + '...'
|
|
124
|
+
: openConversation.lastMessage)
|
|
125
|
+
: 'Continue your conversation';
|
|
126
|
+
|
|
127
|
+
return `
|
|
128
|
+
<button class="messenger-home-message-btn messenger-home-continue-btn" data-conversation-id="${openConversation.id}">
|
|
129
|
+
<div class="messenger-home-continue-info">
|
|
130
|
+
<span class="messenger-home-continue-label">Continue conversation</span>
|
|
131
|
+
<span class="messenger-home-continue-preview">${preview}</span>
|
|
132
|
+
</div>
|
|
133
|
+
<i class="ph ph-arrow-right" style="font-size: 16px;"></i>
|
|
134
|
+
</button>
|
|
135
|
+
`;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return `
|
|
139
|
+
<button class="messenger-home-message-btn">
|
|
140
|
+
<span>Send us a message</span>
|
|
141
|
+
<i class="ph ph-arrow-right" style="font-size: 16px;"></i>
|
|
142
|
+
</button>
|
|
143
|
+
`;
|
|
144
|
+
}
|
|
145
|
+
|
|
121
146
|
_renderFeaturedCard() {
|
|
122
|
-
// Only show if there's featured content configured
|
|
123
147
|
if (!this.options.featuredContent) {
|
|
124
148
|
return '';
|
|
125
149
|
}
|
|
@@ -140,7 +164,6 @@ export class HomeView {
|
|
|
140
164
|
}
|
|
141
165
|
|
|
142
166
|
_renderRecentChangelog() {
|
|
143
|
-
// Show recent changelog preview as cards with images
|
|
144
167
|
const changelogItems = this.state.homeChangelogItems;
|
|
145
168
|
if (changelogItems.length === 0) {
|
|
146
169
|
return '';
|
|
@@ -189,31 +212,40 @@ export class HomeView {
|
|
|
189
212
|
}
|
|
190
213
|
|
|
191
214
|
_attachEvents() {
|
|
192
|
-
// Close button
|
|
193
215
|
this.element
|
|
194
216
|
.querySelector('.messenger-close-btn')
|
|
195
217
|
.addEventListener('click', () => {
|
|
196
218
|
this.state.setOpen(false);
|
|
197
219
|
});
|
|
198
220
|
|
|
199
|
-
// Send message button
|
|
200
|
-
this.element
|
|
201
|
-
|
|
202
|
-
.addEventListener('click', () => {
|
|
203
|
-
|
|
221
|
+
// Send message / continue conversation button
|
|
222
|
+
const msgBtn = this.element.querySelector('.messenger-home-message-btn');
|
|
223
|
+
if (msgBtn) {
|
|
224
|
+
msgBtn.addEventListener('click', () => {
|
|
225
|
+
const convId = msgBtn.dataset.conversationId;
|
|
226
|
+
if (convId) {
|
|
227
|
+
// Continue existing open conversation
|
|
228
|
+
this.state.setActiveConversation(convId);
|
|
229
|
+
this.state.setView('chat');
|
|
230
|
+
if (this.options.onSelectConversation) {
|
|
231
|
+
this.options.onSelectConversation(convId);
|
|
232
|
+
}
|
|
233
|
+
} else {
|
|
234
|
+
// No open conversation — start new
|
|
235
|
+
this.state.setActiveConversation(null);
|
|
236
|
+
this.state.setView('chat');
|
|
237
|
+
}
|
|
204
238
|
});
|
|
239
|
+
}
|
|
205
240
|
|
|
206
|
-
// Changelog items
|
|
207
241
|
this.element
|
|
208
242
|
.querySelectorAll('.messenger-home-changelog-item')
|
|
209
243
|
.forEach((item) => {
|
|
210
244
|
item.addEventListener('click', () => {
|
|
211
|
-
// Navigate to changelog view with specific item selected
|
|
212
245
|
this.state.setView('changelog');
|
|
213
246
|
});
|
|
214
247
|
});
|
|
215
248
|
|
|
216
|
-
// See all changelog
|
|
217
249
|
const seeAllBtn = this.element.querySelector(
|
|
218
250
|
'.messenger-home-changelog-all'
|
|
219
251
|
);
|
|
@@ -223,7 +255,6 @@ export class HomeView {
|
|
|
223
255
|
});
|
|
224
256
|
}
|
|
225
257
|
|
|
226
|
-
// Featured card action
|
|
227
258
|
const featuredBtn = this.element.querySelector(
|
|
228
259
|
'.messenger-home-featured-btn'
|
|
229
260
|
);
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PreChatFormView - Collects user info after the first message
|
|
3
|
+
*/
|
|
4
|
+
export class PreChatFormView {
|
|
5
|
+
constructor(state, options = {}) {
|
|
6
|
+
this.state = state;
|
|
7
|
+
this.options = options;
|
|
8
|
+
this.element = null;
|
|
9
|
+
this._isSubmitting = false;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
render() {
|
|
13
|
+
this.element = document.createElement('div');
|
|
14
|
+
this.element.className = 'messenger-view messenger-prechat-view';
|
|
15
|
+
|
|
16
|
+
this._updateContent();
|
|
17
|
+
|
|
18
|
+
return this.element;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
_updateContent() {
|
|
22
|
+
// Pre-fill from userContext if available
|
|
23
|
+
const existingName = this.state.userContext?.name || '';
|
|
24
|
+
const existingEmail = this.state.userContext?.email || '';
|
|
25
|
+
|
|
26
|
+
this.element.innerHTML = `
|
|
27
|
+
<div class="messenger-prechat-overlay">
|
|
28
|
+
<div class="messenger-prechat-card">
|
|
29
|
+
<h4>Get notified when we reply</h4>
|
|
30
|
+
<form class="messenger-prechat-form" novalidate>
|
|
31
|
+
<div class="messenger-prechat-fields">
|
|
32
|
+
<input
|
|
33
|
+
type="text"
|
|
34
|
+
id="messenger-prechat-name"
|
|
35
|
+
name="name"
|
|
36
|
+
placeholder="Name (optional)"
|
|
37
|
+
value="${this._escapeHtml(existingName)}"
|
|
38
|
+
autocomplete="name"
|
|
39
|
+
class="messenger-prechat-input"
|
|
40
|
+
/>
|
|
41
|
+
<input
|
|
42
|
+
type="email"
|
|
43
|
+
id="messenger-prechat-email"
|
|
44
|
+
name="email"
|
|
45
|
+
placeholder="Email address"
|
|
46
|
+
value="${this._escapeHtml(existingEmail)}"
|
|
47
|
+
required
|
|
48
|
+
autocomplete="email"
|
|
49
|
+
class="messenger-prechat-input"
|
|
50
|
+
/>
|
|
51
|
+
</div>
|
|
52
|
+
<span class="messenger-prechat-error" id="messenger-email-error"></span>
|
|
53
|
+
<div class="messenger-prechat-actions">
|
|
54
|
+
<button type="button" class="messenger-prechat-skip">Skip</button>
|
|
55
|
+
<button type="submit" class="messenger-prechat-submit" disabled>
|
|
56
|
+
<span class="messenger-prechat-submit-text">Continue</span>
|
|
57
|
+
<span class="messenger-prechat-submit-loading" style="display: none;">
|
|
58
|
+
<i class="ph ph-spinner" style="font-size: 16px;"></i>
|
|
59
|
+
</span>
|
|
60
|
+
</button>
|
|
61
|
+
</div>
|
|
62
|
+
</form>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
`;
|
|
66
|
+
|
|
67
|
+
this._attachEvents();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
_renderTeamAvatars() {
|
|
71
|
+
const avatars = this.state.teamAvatars;
|
|
72
|
+
if (!avatars || avatars.length === 0) {
|
|
73
|
+
return `
|
|
74
|
+
<div class="messenger-avatar-stack">
|
|
75
|
+
<div class="messenger-avatar" style="background: #5856d6;">S</div>
|
|
76
|
+
<div class="messenger-avatar" style="background: #007aff;">T</div>
|
|
77
|
+
</div>
|
|
78
|
+
`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const colors = ['#5856d6', '#007aff', '#34c759', '#ff9500', '#ff3b30'];
|
|
82
|
+
const avatarItems = avatars
|
|
83
|
+
.slice(0, 3)
|
|
84
|
+
.map((avatar, i) => {
|
|
85
|
+
if (typeof avatar === 'string' && avatar.startsWith('http')) {
|
|
86
|
+
return `<img class="messenger-avatar" src="${avatar}" alt="Team member" style="z-index: ${3 - i};" />`;
|
|
87
|
+
}
|
|
88
|
+
return `<div class="messenger-avatar" style="background: ${colors[i % colors.length]}; z-index: ${3 - i};">${avatar.charAt(0).toUpperCase()}</div>`;
|
|
89
|
+
})
|
|
90
|
+
.join('');
|
|
91
|
+
|
|
92
|
+
return `<div class="messenger-avatar-stack">${avatarItems}</div>`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
_escapeHtml(text) {
|
|
96
|
+
if (!text) return '';
|
|
97
|
+
return text
|
|
98
|
+
.replace(/&/g, '&')
|
|
99
|
+
.replace(/</g, '<')
|
|
100
|
+
.replace(/>/g, '>')
|
|
101
|
+
.replace(/"/g, '"');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
_attachEvents() {
|
|
105
|
+
// Form validation
|
|
106
|
+
const form = this.element.querySelector('.messenger-prechat-form');
|
|
107
|
+
const emailInput = this.element.querySelector('#messenger-prechat-email');
|
|
108
|
+
const submitBtn = this.element.querySelector('.messenger-prechat-submit');
|
|
109
|
+
const skipBtn = this.element.querySelector('.messenger-prechat-skip');
|
|
110
|
+
|
|
111
|
+
const validateForm = () => {
|
|
112
|
+
const email = emailInput.value.trim();
|
|
113
|
+
const isEmailValid = this._isValidEmail(email);
|
|
114
|
+
submitBtn.disabled = !isEmailValid;
|
|
115
|
+
return isEmailValid;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
emailInput.addEventListener('input', () => {
|
|
119
|
+
this._clearError('messenger-email-error');
|
|
120
|
+
validateForm();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
emailInput.addEventListener('blur', () => {
|
|
124
|
+
const email = emailInput.value.trim();
|
|
125
|
+
if (email && !this._isValidEmail(email)) {
|
|
126
|
+
this._showError('messenger-email-error', 'Please enter a valid email');
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Skip button - go back to chat without collecting info
|
|
131
|
+
skipBtn.addEventListener('click', () => {
|
|
132
|
+
this.state.setView('chat');
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Form submission
|
|
136
|
+
form.addEventListener('submit', async (e) => {
|
|
137
|
+
e.preventDefault();
|
|
138
|
+
if (this._isSubmitting) return;
|
|
139
|
+
if (!validateForm()) {
|
|
140
|
+
this._showError('messenger-email-error', 'Please enter a valid email');
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
await this._handleSubmit();
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Set initial button state
|
|
147
|
+
validateForm();
|
|
148
|
+
|
|
149
|
+
// Focus email input
|
|
150
|
+
setTimeout(() => emailInput.focus(), 100);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
_isValidEmail(email) {
|
|
154
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
155
|
+
return emailRegex.test(email);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
_showError(elementId, message) {
|
|
159
|
+
const errorEl = this.element.querySelector(`#${elementId}`);
|
|
160
|
+
if (errorEl) {
|
|
161
|
+
errorEl.textContent = message;
|
|
162
|
+
errorEl.style.display = 'block';
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
_clearError(elementId) {
|
|
167
|
+
const errorEl = this.element.querySelector(`#${elementId}`);
|
|
168
|
+
if (errorEl) {
|
|
169
|
+
errorEl.textContent = '';
|
|
170
|
+
errorEl.style.display = 'none';
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async _handleSubmit() {
|
|
175
|
+
const nameInput = this.element.querySelector('#messenger-prechat-name');
|
|
176
|
+
const emailInput = this.element.querySelector('#messenger-prechat-email');
|
|
177
|
+
const submitBtn = this.element.querySelector('.messenger-prechat-submit');
|
|
178
|
+
const submitText = submitBtn.querySelector('.messenger-prechat-submit-text');
|
|
179
|
+
const submitLoading = submitBtn.querySelector('.messenger-prechat-submit-loading');
|
|
180
|
+
|
|
181
|
+
const name = nameInput.value.trim();
|
|
182
|
+
const email = emailInput.value.trim();
|
|
183
|
+
|
|
184
|
+
// Show loading state
|
|
185
|
+
this._isSubmitting = true;
|
|
186
|
+
submitBtn.disabled = true;
|
|
187
|
+
submitText.style.display = 'none';
|
|
188
|
+
submitLoading.style.display = 'inline-flex';
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
// First, identify the contact with collected info
|
|
192
|
+
if (this.options.onIdentifyContact) {
|
|
193
|
+
await this.options.onIdentifyContact({ name, email });
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Update state with user info
|
|
197
|
+
if (!this.state.userContext) {
|
|
198
|
+
this.state.userContext = {};
|
|
199
|
+
}
|
|
200
|
+
this.state.userContext.name = name;
|
|
201
|
+
this.state.userContext.email = email;
|
|
202
|
+
|
|
203
|
+
this._isSubmitting = false;
|
|
204
|
+
|
|
205
|
+
// Go to chat after collecting contact info
|
|
206
|
+
this.state.setView('chat');
|
|
207
|
+
} catch (error) {
|
|
208
|
+
console.error('[PreChatFormView] Error submitting form:', error);
|
|
209
|
+
this._showError('messenger-email-error', 'Something went wrong. Please try again.');
|
|
210
|
+
|
|
211
|
+
// Reset button state
|
|
212
|
+
this._isSubmitting = false;
|
|
213
|
+
submitBtn.disabled = false;
|
|
214
|
+
submitText.style.display = 'inline';
|
|
215
|
+
submitLoading.style.display = 'none';
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
destroy() {
|
|
220
|
+
if (this.element && this.element.parentNode) {
|
|
221
|
+
this.element.parentNode.removeChild(this.element);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|