@product7/feedback-sdk 1.3.3 → 1.3.5
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 +2441 -3240
- 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 +1 -1
- package/src/api/services/MessengerService.js +76 -0
- package/src/core/BaseAPIService.js +4 -0
- package/src/index.js +2 -3
- package/src/styles/base.js +27 -52
- package/src/styles/changelog.js +152 -269
- package/src/styles/components.js +489 -0
- package/src/styles/design-tokens.js +104 -0
- package/src/styles/feedback.js +59 -369
- package/src/styles/messenger-core.js +390 -0
- package/src/styles/messenger-features.js +347 -0
- package/src/styles/messenger-help.js +298 -0
- package/src/styles/messenger-themes.js +500 -0
- package/src/styles/messenger-views.js +618 -0
- package/src/styles/messenger.js +558 -0
- package/src/styles/styles.js +24 -2
- package/src/styles/surveys.js +354 -0
- package/src/widgets/BaseWidget.js +25 -58
- package/src/widgets/ButtonWidget.js +3 -18
- package/src/widgets/ChangelogWidget.js +7 -48
- package/src/widgets/InlineWidget.js +16 -13
- package/src/widgets/MessengerWidget.js +37 -51
- package/src/widgets/SurveyWidget.js +42 -146
- package/src/widgets/TabWidget.js +2 -22
- package/src/widgets/messenger/MessengerState.js +49 -46
- package/src/widgets/messenger/components/MessengerLauncher.js +10 -5
- package/src/widgets/messenger/components/MessengerPanel.js +5 -27
- package/src/widgets/messenger/components/NavigationTabs.js +5 -14
- package/src/widgets/messenger/views/ChangelogView.js +13 -14
- package/src/widgets/messenger/views/ChatView.js +43 -36
- package/src/widgets/messenger/views/ConversationsView.js +16 -21
- package/src/widgets/messenger/views/HelpView.js +10 -10
- package/src/widgets/messenger/views/HomeView.js +11 -16
- package/src/widgets/messenger/views/PreChatFormView.js +18 -30
- package/src/styles/messengerStyles.js +0 -2328
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
export const surveyStyles = `
|
|
2
|
+
.feedback-survey-container {
|
|
3
|
+
display: none;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.feedback-survey-backdrop {
|
|
7
|
+
position: fixed;
|
|
8
|
+
top: 0;
|
|
9
|
+
left: 0;
|
|
10
|
+
right: 0;
|
|
11
|
+
bottom: 0;
|
|
12
|
+
background: rgba(0, 0, 0, 0.5);
|
|
13
|
+
z-index: var(--z-modal-backdrop);
|
|
14
|
+
animation: fadeIn var(--transition-slow);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.feedback-survey {
|
|
18
|
+
position: fixed;
|
|
19
|
+
z-index: var(--z-modal);
|
|
20
|
+
background: var(--color-white);
|
|
21
|
+
border-radius: var(--radius-2xl);
|
|
22
|
+
box-shadow: var(--shadow-2xl);
|
|
23
|
+
padding: var(--spacing-6);
|
|
24
|
+
min-width: 320px;
|
|
25
|
+
max-width: 400px;
|
|
26
|
+
font-family: inherit;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.feedback-survey-bottom-right {
|
|
30
|
+
bottom: var(--spacing-6);
|
|
31
|
+
right: var(--spacing-6);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.feedback-survey-bottom-left {
|
|
35
|
+
bottom: var(--spacing-6);
|
|
36
|
+
left: var(--spacing-6);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.feedback-survey-center {
|
|
40
|
+
top: 50%;
|
|
41
|
+
left: 50%;
|
|
42
|
+
transform: translate(-50%, -50%);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.feedback-survey-bottom {
|
|
46
|
+
bottom: 0;
|
|
47
|
+
left: 0;
|
|
48
|
+
right: 0;
|
|
49
|
+
border-radius: var(--radius-2xl) var(--radius-2xl) 0 0;
|
|
50
|
+
max-width: none;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.feedback-survey-close {
|
|
54
|
+
position: absolute;
|
|
55
|
+
top: var(--spacing-3);
|
|
56
|
+
right: var(--spacing-3);
|
|
57
|
+
background: none;
|
|
58
|
+
border: none;
|
|
59
|
+
font-size: 20px;
|
|
60
|
+
cursor: pointer;
|
|
61
|
+
color: var(--color-neutral-500);
|
|
62
|
+
line-height: 1;
|
|
63
|
+
width: 28px;
|
|
64
|
+
height: 28px;
|
|
65
|
+
display: flex;
|
|
66
|
+
align-items: center;
|
|
67
|
+
justify-content: center;
|
|
68
|
+
border-radius: var(--radius-sm);
|
|
69
|
+
transition: all var(--transition-base);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.feedback-survey-close:hover {
|
|
73
|
+
background: var(--color-neutral-100);
|
|
74
|
+
color: var(--color-neutral-900);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.feedback-survey-title {
|
|
78
|
+
margin: 0 0 var(--spacing-2) 0;
|
|
79
|
+
font-size: var(--font-size-xl);
|
|
80
|
+
font-weight: var(--font-weight-semibold);
|
|
81
|
+
padding-right: var(--spacing-6);
|
|
82
|
+
color: var(--color-text-primary);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.feedback-survey-description {
|
|
86
|
+
color: var(--color-text-secondary);
|
|
87
|
+
margin: 0 0 var(--spacing-5) 0;
|
|
88
|
+
font-size: var(--font-size-base);
|
|
89
|
+
line-height: var(--line-height-relaxed);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.feedback-survey-content {
|
|
93
|
+
margin-bottom: var(--spacing-4);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.feedback-survey-nps {
|
|
97
|
+
display: flex;
|
|
98
|
+
justify-content: space-between;
|
|
99
|
+
gap: var(--spacing-1);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.feedback-survey-nps-btn {
|
|
103
|
+
width: 28px;
|
|
104
|
+
height: 36px;
|
|
105
|
+
border: 1px solid var(--color-border);
|
|
106
|
+
border-radius: var(--radius-sm);
|
|
107
|
+
background: var(--color-surface);
|
|
108
|
+
cursor: pointer;
|
|
109
|
+
font-size: var(--font-size-sm);
|
|
110
|
+
color: var(--color-text-primary);
|
|
111
|
+
transition: all var(--transition-fast);
|
|
112
|
+
font-family: inherit;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.feedback-survey-nps-btn:hover {
|
|
116
|
+
border-color: var(--color-primary);
|
|
117
|
+
background: var(--color-white);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.feedback-survey-nps-btn.selected {
|
|
121
|
+
background: var(--color-primary);
|
|
122
|
+
border-color: var(--color-primary);
|
|
123
|
+
color: var(--color-white);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.feedback-survey-csat {
|
|
127
|
+
display: flex;
|
|
128
|
+
justify-content: center;
|
|
129
|
+
gap: var(--spacing-4);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.feedback-survey-csat-btn {
|
|
133
|
+
background: none;
|
|
134
|
+
border: none;
|
|
135
|
+
cursor: pointer;
|
|
136
|
+
font-size: 36px;
|
|
137
|
+
transition: transform var(--transition-fast);
|
|
138
|
+
padding: var(--spacing-2);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.feedback-survey-csat-btn:hover {
|
|
142
|
+
transform: scale(1.1);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.feedback-survey-csat-btn.selected {
|
|
146
|
+
transform: scale(1.2);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.feedback-survey-ces {
|
|
150
|
+
display: flex;
|
|
151
|
+
justify-content: space-between;
|
|
152
|
+
gap: var(--spacing-2);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.feedback-survey-ces-btn {
|
|
156
|
+
flex: 1;
|
|
157
|
+
padding: var(--spacing-3) var(--spacing-2);
|
|
158
|
+
border: 1px solid var(--color-border);
|
|
159
|
+
border-radius: var(--radius-md);
|
|
160
|
+
background: var(--color-surface);
|
|
161
|
+
cursor: pointer;
|
|
162
|
+
font-size: var(--font-size-xs);
|
|
163
|
+
color: var(--color-text-primary);
|
|
164
|
+
transition: all var(--transition-fast);
|
|
165
|
+
font-family: inherit;
|
|
166
|
+
text-align: center;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.feedback-survey-ces-btn:hover {
|
|
170
|
+
border-color: var(--color-primary);
|
|
171
|
+
background: var(--color-white);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.feedback-survey-ces-btn.selected {
|
|
175
|
+
background: var(--color-primary);
|
|
176
|
+
border-color: var(--color-primary);
|
|
177
|
+
color: var(--color-white);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.feedback-survey-labels {
|
|
181
|
+
display: flex;
|
|
182
|
+
justify-content: space-between;
|
|
183
|
+
margin-top: var(--spacing-2);
|
|
184
|
+
font-size: var(--font-size-xs);
|
|
185
|
+
color: var(--color-text-secondary);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.feedback-survey-frequency {
|
|
189
|
+
display: flex;
|
|
190
|
+
gap: var(--spacing-2);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.feedback-survey-freq-btn {
|
|
194
|
+
flex: 1;
|
|
195
|
+
padding: 10px;
|
|
196
|
+
border: 1px solid var(--color-border);
|
|
197
|
+
border-radius: var(--radius-md);
|
|
198
|
+
background: var(--color-surface);
|
|
199
|
+
cursor: pointer;
|
|
200
|
+
font-size: var(--font-size-sm);
|
|
201
|
+
color: var(--color-text-primary);
|
|
202
|
+
transition: all var(--transition-fast);
|
|
203
|
+
font-family: inherit;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.feedback-survey-freq-btn:hover {
|
|
207
|
+
border-color: var(--color-primary);
|
|
208
|
+
background: var(--color-white);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.feedback-survey-freq-btn.selected {
|
|
212
|
+
background: var(--color-primary);
|
|
213
|
+
border-color: var(--color-primary);
|
|
214
|
+
color: var(--color-white);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.feedback-survey-feedback {
|
|
218
|
+
margin-top: var(--spacing-4);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.feedback-survey-textarea {
|
|
222
|
+
width: 100%;
|
|
223
|
+
padding: var(--spacing-3) 14px;
|
|
224
|
+
border: 1px solid var(--color-border);
|
|
225
|
+
border-radius: var(--radius-md);
|
|
226
|
+
font-size: var(--font-size-base);
|
|
227
|
+
resize: none;
|
|
228
|
+
height: 80px;
|
|
229
|
+
background: var(--color-white);
|
|
230
|
+
color: var(--color-text-primary);
|
|
231
|
+
font-family: inherit;
|
|
232
|
+
box-sizing: border-box;
|
|
233
|
+
transition: all var(--transition-base);
|
|
234
|
+
outline: none;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.feedback-survey-textarea::placeholder {
|
|
238
|
+
color: var(--color-text-tertiary);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.feedback-survey-textarea:focus {
|
|
242
|
+
border-color: var(--color-primary);
|
|
243
|
+
box-shadow: 0 0 0 3px var(--color-primary-light);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.feedback-survey-select {
|
|
247
|
+
width: 100%;
|
|
248
|
+
padding: 10px 14px;
|
|
249
|
+
border: 1px solid var(--color-border);
|
|
250
|
+
border-radius: var(--radius-md);
|
|
251
|
+
font-size: var(--font-size-base);
|
|
252
|
+
background: var(--color-white);
|
|
253
|
+
color: var(--color-text-primary);
|
|
254
|
+
font-family: inherit;
|
|
255
|
+
cursor: pointer;
|
|
256
|
+
transition: all var(--transition-base);
|
|
257
|
+
outline: none;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.feedback-survey-select:focus {
|
|
261
|
+
border-color: var(--color-primary);
|
|
262
|
+
box-shadow: 0 0 0 3px var(--color-primary-light);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.feedback-survey-input {
|
|
266
|
+
width: 100%;
|
|
267
|
+
padding: 10px 14px;
|
|
268
|
+
border: 1px solid var(--color-border);
|
|
269
|
+
border-radius: var(--radius-md);
|
|
270
|
+
font-size: var(--font-size-base);
|
|
271
|
+
background: var(--color-white);
|
|
272
|
+
color: var(--color-text-primary);
|
|
273
|
+
font-family: inherit;
|
|
274
|
+
box-sizing: border-box;
|
|
275
|
+
transition: all var(--transition-base);
|
|
276
|
+
outline: none;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.feedback-survey-input::placeholder {
|
|
280
|
+
color: var(--color-text-tertiary);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.feedback-survey-input:focus {
|
|
284
|
+
border-color: var(--color-primary);
|
|
285
|
+
box-shadow: 0 0 0 3px var(--color-primary-light);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.feedback-survey-submit {
|
|
289
|
+
width: 100%;
|
|
290
|
+
margin-top: var(--spacing-3);
|
|
291
|
+
padding: var(--spacing-3);
|
|
292
|
+
background: var(--color-primary);
|
|
293
|
+
color: var(--color-white);
|
|
294
|
+
border: none;
|
|
295
|
+
border-radius: var(--radius-md);
|
|
296
|
+
font-size: var(--font-size-base);
|
|
297
|
+
font-weight: var(--font-weight-medium);
|
|
298
|
+
cursor: pointer;
|
|
299
|
+
font-family: inherit;
|
|
300
|
+
transition: all var(--transition-base);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.feedback-survey-submit:hover {
|
|
304
|
+
background: var(--color-primary-hover);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
.feedback-survey-error {
|
|
308
|
+
color: var(--color-error);
|
|
309
|
+
font-size: var(--font-size-sm);
|
|
310
|
+
margin-top: var(--spacing-2);
|
|
311
|
+
text-align: center;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
.feedback-survey-success {
|
|
315
|
+
position: fixed;
|
|
316
|
+
top: var(--spacing-6);
|
|
317
|
+
right: var(--spacing-6);
|
|
318
|
+
background: var(--color-success);
|
|
319
|
+
color: var(--color-white);
|
|
320
|
+
padding: var(--spacing-4) var(--spacing-6);
|
|
321
|
+
border-radius: var(--radius-xl);
|
|
322
|
+
font-size: var(--font-size-base);
|
|
323
|
+
font-weight: var(--font-weight-medium);
|
|
324
|
+
z-index: var(--z-notification);
|
|
325
|
+
box-shadow: var(--shadow-lg);
|
|
326
|
+
font-family: inherit;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.feedback-survey-success > div {
|
|
330
|
+
display: flex;
|
|
331
|
+
align-items: center;
|
|
332
|
+
gap: var(--spacing-2);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
@media (max-width: 768px) {
|
|
336
|
+
.feedback-survey {
|
|
337
|
+
min-width: 300px;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.feedback-survey-bottom {
|
|
341
|
+
padding: var(--spacing-5);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.feedback-survey-nps-btn {
|
|
345
|
+
width: 24px;
|
|
346
|
+
height: 32px;
|
|
347
|
+
font-size: var(--font-size-xs);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.feedback-survey-csat-btn {
|
|
351
|
+
font-size: 32px;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
`;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export class BaseWidget {
|
|
2
2
|
static STORAGE_KEY = 'feedback_submitted';
|
|
3
|
-
static DEFAULT_COOLDOWN_DAYS = 30;
|
|
3
|
+
static DEFAULT_COOLDOWN_DAYS = 30;
|
|
4
4
|
|
|
5
5
|
constructor(options = {}) {
|
|
6
6
|
this.id = options.id;
|
|
@@ -14,13 +14,13 @@ export class BaseWidget {
|
|
|
14
14
|
boardId: this.sdk.config.boardId,
|
|
15
15
|
displayMode: options.displayMode || 'panel',
|
|
16
16
|
size: options.size || 'medium',
|
|
17
|
-
primaryColor: options.primaryColor || '#
|
|
17
|
+
primaryColor: options.primaryColor || '#155EEF',
|
|
18
18
|
backgroundColor: options.backgroundColor || '#ffffff',
|
|
19
19
|
textColor: options.textColor || '#1F2937',
|
|
20
20
|
autoShow: false,
|
|
21
21
|
showBackdrop: true,
|
|
22
22
|
customStyles: {},
|
|
23
|
-
suppressAfterSubmission: true,
|
|
23
|
+
suppressAfterSubmission: true,
|
|
24
24
|
suppressionDays: BaseWidget.DEFAULT_COOLDOWN_DAYS,
|
|
25
25
|
...options,
|
|
26
26
|
};
|
|
@@ -48,7 +48,6 @@ export class BaseWidget {
|
|
|
48
48
|
mount(container) {
|
|
49
49
|
if (this.mounted || this.destroyed) return this;
|
|
50
50
|
|
|
51
|
-
// Check if feedback was recently submitted and should be suppressed
|
|
52
51
|
if (this.options.suppressAfterSubmission && this._hasRecentlySubmitted()) {
|
|
53
52
|
this.sdk.eventBus.emit('widget:suppressed', {
|
|
54
53
|
widget: this,
|
|
@@ -100,7 +99,6 @@ export class BaseWidget {
|
|
|
100
99
|
|
|
101
100
|
if (this.options.displayMode === 'modal') {
|
|
102
101
|
this._showLoadingModal();
|
|
103
|
-
// Simulate loading delay then show form
|
|
104
102
|
setTimeout(() => {
|
|
105
103
|
this._hideLoadingModal();
|
|
106
104
|
this._renderPanel();
|
|
@@ -129,16 +127,14 @@ export class BaseWidget {
|
|
|
129
127
|
_showLoadingModal() {
|
|
130
128
|
this.state.isLoading = true;
|
|
131
129
|
|
|
132
|
-
// Create backdrop
|
|
133
130
|
this.backdropElement = document.createElement('div');
|
|
134
|
-
this.backdropElement.className = '
|
|
131
|
+
this.backdropElement.className = 'sdk-modal-backdrop';
|
|
135
132
|
document.body.appendChild(this.backdropElement);
|
|
136
133
|
|
|
137
|
-
// Create loading modal
|
|
138
134
|
this.loadingElement = document.createElement('div');
|
|
139
|
-
this.loadingElement.className =
|
|
135
|
+
this.loadingElement.className = 'feedback-loading-modal';
|
|
140
136
|
this.loadingElement.innerHTML = `
|
|
141
|
-
<div class="
|
|
137
|
+
<div class="sdk-spinner"></div>
|
|
142
138
|
`;
|
|
143
139
|
document.body.appendChild(this.loadingElement);
|
|
144
140
|
|
|
@@ -202,7 +198,6 @@ export class BaseWidget {
|
|
|
202
198
|
|
|
203
199
|
const response = await this.apiService.submitFeedback(payload);
|
|
204
200
|
|
|
205
|
-
// Track that feedback was submitted
|
|
206
201
|
this._trackSubmission();
|
|
207
202
|
|
|
208
203
|
this._showSuccessMessage();
|
|
@@ -246,9 +241,6 @@ export class BaseWidget {
|
|
|
246
241
|
onMount() {}
|
|
247
242
|
onDestroy() {}
|
|
248
243
|
|
|
249
|
-
/**
|
|
250
|
-
* Track that feedback was submitted to localStorage
|
|
251
|
-
*/
|
|
252
244
|
_trackSubmission() {
|
|
253
245
|
try {
|
|
254
246
|
const workspace = this.sdk.config.workspace;
|
|
@@ -259,21 +251,14 @@ export class BaseWidget {
|
|
|
259
251
|
};
|
|
260
252
|
localStorage.setItem(storageKey, JSON.stringify(data));
|
|
261
253
|
} catch (e) {
|
|
262
|
-
// localStorage may not be available
|
|
263
254
|
console.warn('Failed to track feedback submission:', e);
|
|
264
255
|
}
|
|
265
256
|
}
|
|
266
257
|
|
|
267
|
-
/**
|
|
268
|
-
* Check if feedback was recently submitted (within cooldown period)
|
|
269
|
-
* Uses backend tracking (preferred) with localStorage as fallback
|
|
270
|
-
* @returns {boolean} true if feedback was submitted within the cooldown period
|
|
271
|
-
*/
|
|
272
258
|
_hasRecentlySubmitted() {
|
|
273
259
|
const cooldownMs = this.options.suppressionDays * 24 * 60 * 60 * 1000;
|
|
274
260
|
const now = Date.now();
|
|
275
261
|
|
|
276
|
-
// Check backend tracking first (from init response)
|
|
277
262
|
if (this.sdk.config.last_feedback_at) {
|
|
278
263
|
try {
|
|
279
264
|
const backendTimestamp = new Date(
|
|
@@ -287,7 +272,6 @@ export class BaseWidget {
|
|
|
287
272
|
}
|
|
288
273
|
}
|
|
289
274
|
|
|
290
|
-
// Fallback to localStorage
|
|
291
275
|
try {
|
|
292
276
|
const workspace = this.sdk.config.workspace;
|
|
293
277
|
const storageKey = `${BaseWidget.STORAGE_KEY}_${workspace}`;
|
|
@@ -300,14 +284,10 @@ export class BaseWidget {
|
|
|
300
284
|
|
|
301
285
|
return now - submittedAt < cooldownMs;
|
|
302
286
|
} catch (e) {
|
|
303
|
-
// localStorage may not be available or data is corrupted
|
|
304
287
|
return false;
|
|
305
288
|
}
|
|
306
289
|
}
|
|
307
290
|
|
|
308
|
-
/**
|
|
309
|
-
* Clear the submission tracking (allow showing the widget again)
|
|
310
|
-
*/
|
|
311
291
|
clearSubmissionTracking() {
|
|
312
292
|
try {
|
|
313
293
|
const workspace = this.sdk.config.workspace;
|
|
@@ -318,10 +298,6 @@ export class BaseWidget {
|
|
|
318
298
|
}
|
|
319
299
|
}
|
|
320
300
|
|
|
321
|
-
/**
|
|
322
|
-
* Check if the widget should be shown based on submission history
|
|
323
|
-
* @returns {boolean} true if the widget should be shown
|
|
324
|
-
*/
|
|
325
301
|
shouldShow() {
|
|
326
302
|
if (!this.options.suppressAfterSubmission) return true;
|
|
327
303
|
return !this._hasRecentlySubmitted();
|
|
@@ -346,7 +322,7 @@ export class BaseWidget {
|
|
|
346
322
|
|
|
347
323
|
if (this.options.showBackdrop && !this.backdropElement) {
|
|
348
324
|
this.backdropElement = document.createElement('div');
|
|
349
|
-
this.backdropElement.className = '
|
|
325
|
+
this.backdropElement.className = 'sdk-modal-backdrop';
|
|
350
326
|
document.body.appendChild(this.backdropElement);
|
|
351
327
|
|
|
352
328
|
this.backdropElement.addEventListener('click', this.closePanel);
|
|
@@ -384,32 +360,34 @@ export class BaseWidget {
|
|
|
384
360
|
<div class="feedback-panel-content">
|
|
385
361
|
<div class="feedback-panel-header">
|
|
386
362
|
<h3>Send Feedback</h3>
|
|
387
|
-
<button class="
|
|
363
|
+
<button class="sdk-close-btn" type="button" aria-label="Close">×</button>
|
|
388
364
|
</div>
|
|
389
365
|
<div class="feedback-panel-body">
|
|
390
366
|
<form class="feedback-form">
|
|
391
|
-
<div class="
|
|
392
|
-
<label for="feedback-title-${this.id}">Title (optional)</label>
|
|
367
|
+
<div class="sdk-form-group">
|
|
368
|
+
<label class="sdk-label" for="feedback-title-${this.id}">Title (optional)</label>
|
|
393
369
|
<input
|
|
394
370
|
type="text"
|
|
395
371
|
id="feedback-title-${this.id}"
|
|
396
372
|
name="title"
|
|
373
|
+
class="sdk-input"
|
|
397
374
|
placeholder="Brief description of your feedback"
|
|
398
375
|
value="${this.state.title}"
|
|
399
376
|
/>
|
|
400
377
|
</div>
|
|
401
|
-
<div class="
|
|
402
|
-
<label for="feedback-content-${this.id}">Message *</label>
|
|
378
|
+
<div class="sdk-form-group">
|
|
379
|
+
<label class="sdk-label" for="feedback-content-${this.id}">Message *</label>
|
|
403
380
|
<textarea
|
|
404
381
|
id="feedback-content-${this.id}"
|
|
405
382
|
name="content"
|
|
383
|
+
class="sdk-textarea"
|
|
406
384
|
placeholder="Tell us what you think..."
|
|
407
385
|
required
|
|
408
386
|
>${this.state.content}</textarea>
|
|
409
387
|
</div>
|
|
410
388
|
<div class="feedback-error" role="alert"></div>
|
|
411
389
|
<div class="feedback-form-actions">
|
|
412
|
-
<button type="submit" class="
|
|
390
|
+
<button type="submit" class="sdk-btn-primary sdk-btn-block">
|
|
413
391
|
${this.state.isSubmitting ? 'Sending...' : 'Send Feedback'}
|
|
414
392
|
</button>
|
|
415
393
|
</div>
|
|
@@ -423,7 +401,7 @@ export class BaseWidget {
|
|
|
423
401
|
const panel = this.panelElement;
|
|
424
402
|
|
|
425
403
|
panel
|
|
426
|
-
.querySelector('.
|
|
404
|
+
.querySelector('.sdk-close-btn')
|
|
427
405
|
.addEventListener('click', this.closePanel);
|
|
428
406
|
|
|
429
407
|
const form = panel.querySelector('.feedback-form');
|
|
@@ -455,7 +433,7 @@ export class BaseWidget {
|
|
|
455
433
|
|
|
456
434
|
_updateSubmitButton() {
|
|
457
435
|
if (this.panelElement) {
|
|
458
|
-
const submitBtn = this.panelElement.querySelector('.
|
|
436
|
+
const submitBtn = this.panelElement.querySelector('.sdk-btn-primary');
|
|
459
437
|
if (submitBtn) {
|
|
460
438
|
submitBtn.textContent = this.state.isSubmitting
|
|
461
439
|
? 'Sending...'
|
|
@@ -486,18 +464,18 @@ export class BaseWidget {
|
|
|
486
464
|
|
|
487
465
|
_showSuccessMessage() {
|
|
488
466
|
const notification = document.createElement('div');
|
|
489
|
-
notification.className = '
|
|
467
|
+
notification.className = 'sdk-notification sdk-notification-success';
|
|
490
468
|
notification.innerHTML = `
|
|
491
|
-
<div class="
|
|
492
|
-
<div class="
|
|
493
|
-
<span>Feedback submitted successfully!</span>
|
|
494
|
-
<button class="
|
|
469
|
+
<div class="sdk-notification-content">
|
|
470
|
+
<div class="sdk-notification-icon">✓</div>
|
|
471
|
+
<span class="sdk-notification-text">Feedback submitted successfully!</span>
|
|
472
|
+
<button class="sdk-close-btn" aria-label="Close">×</button>
|
|
495
473
|
</div>
|
|
496
474
|
`;
|
|
497
475
|
|
|
498
476
|
document.body.appendChild(notification);
|
|
499
477
|
|
|
500
|
-
const closeBtn = notification.querySelector('.
|
|
478
|
+
const closeBtn = notification.querySelector('.sdk-close-btn');
|
|
501
479
|
const closeNotification = () => {
|
|
502
480
|
if (notification.parentNode) {
|
|
503
481
|
notification.style.opacity = '0';
|
|
@@ -522,18 +500,7 @@ export class BaseWidget {
|
|
|
522
500
|
}
|
|
523
501
|
|
|
524
502
|
_updateTheme() {
|
|
525
|
-
|
|
526
|
-
this.element.className = this.element.className.replace(
|
|
527
|
-
/theme-\w+/,
|
|
528
|
-
`theme-${this.options.theme}`
|
|
529
|
-
);
|
|
530
|
-
}
|
|
531
|
-
if (this.panelElement) {
|
|
532
|
-
this.panelElement.className = this.panelElement.className.replace(
|
|
533
|
-
/theme-\w+/,
|
|
534
|
-
`theme-${this.options.theme}`
|
|
535
|
-
);
|
|
536
|
-
}
|
|
503
|
+
// No longer needed - single theme
|
|
537
504
|
}
|
|
538
505
|
|
|
539
506
|
openModal() {
|
|
@@ -543,4 +510,4 @@ export class BaseWidget {
|
|
|
543
510
|
closeModal() {
|
|
544
511
|
this.closePanel();
|
|
545
512
|
}
|
|
546
|
-
}
|
|
513
|
+
}
|
|
@@ -8,7 +8,7 @@ export class ButtonWidget extends BaseWidget {
|
|
|
8
8
|
|
|
9
9
|
_render() {
|
|
10
10
|
const button = document.createElement('div');
|
|
11
|
-
button.className = `feedback-widget
|
|
11
|
+
button.className = `feedback-widget-button position-${this.options.position}`;
|
|
12
12
|
button.innerHTML = `
|
|
13
13
|
<button class="feedback-trigger-btn" type="button">
|
|
14
14
|
<svg class="feedback-icon" width="20" height="20" viewBox="0 0 256 256" fill="currentColor">
|
|
@@ -42,7 +42,6 @@ export class ButtonWidget extends BaseWidget {
|
|
|
42
42
|
const minimizeIcon = this.element.querySelector('.feedback-minimize-icon');
|
|
43
43
|
const expandIcon = this.element.querySelector('.feedback-expand-icon');
|
|
44
44
|
|
|
45
|
-
// Add click handlers directly to the icons
|
|
46
45
|
minimizeIcon.addEventListener('click', (e) => {
|
|
47
46
|
e.stopPropagation();
|
|
48
47
|
e.preventDefault();
|
|
@@ -55,32 +54,18 @@ export class ButtonWidget extends BaseWidget {
|
|
|
55
54
|
this.restore();
|
|
56
55
|
});
|
|
57
56
|
|
|
58
|
-
// Main button click handler
|
|
59
57
|
button.addEventListener('click', (e) => {
|
|
60
|
-
// Check if the click originated from an icon
|
|
61
58
|
if (
|
|
62
59
|
e.target.closest('.feedback-minimize-icon') ||
|
|
63
60
|
e.target.closest('.feedback-expand-icon')
|
|
64
61
|
) {
|
|
65
|
-
return;
|
|
62
|
+
return;
|
|
66
63
|
}
|
|
67
64
|
|
|
68
65
|
if (!this.isMinimized) {
|
|
69
66
|
this.openPanel();
|
|
70
67
|
}
|
|
71
68
|
});
|
|
72
|
-
|
|
73
|
-
button.addEventListener('mouseenter', () => {
|
|
74
|
-
if (!this.state.isSubmitting && !this.isMinimized) {
|
|
75
|
-
button.style.transform = 'translateY(-2px)';
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
button.addEventListener('mouseleave', () => {
|
|
80
|
-
if (!this.isMinimized) {
|
|
81
|
-
button.style.transform = 'translateY(0)';
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
69
|
}
|
|
85
70
|
|
|
86
71
|
minimize() {
|
|
@@ -113,4 +98,4 @@ export class ButtonWidget extends BaseWidget {
|
|
|
113
98
|
);
|
|
114
99
|
}
|
|
115
100
|
}
|
|
116
|
-
}
|
|
101
|
+
}
|