@product7/feedback-sdk 1.3.0 → 1.3.2
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 +2158 -663
- 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/core/APIService.js +191 -5
- package/src/core/FeedbackSDK.js +3 -0
- package/src/styles/messengerStyles.js +580 -9
- package/src/widgets/MessengerWidget.js +247 -137
- package/src/widgets/messenger/MessengerState.js +31 -1
- package/src/widgets/messenger/views/ChatView.js +347 -29
- package/src/widgets/messenger/views/ConversationsView.js +20 -5
- package/src/widgets/messenger/views/HomeView.js +50 -10
- package/src/widgets/messenger/views/PreChatFormView.js +224 -0
|
@@ -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
|
+
}
|