@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.
@@ -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, '&amp;')
99
+ .replace(/</g, '&lt;')
100
+ .replace(/>/g, '&gt;')
101
+ .replace(/"/g, '&quot;');
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
+ }