@product7/feedback-sdk 1.0.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.
@@ -0,0 +1,557 @@
1
+ export const CSS_STYLES = `
2
+ .feedback-widget {
3
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', Oxygen, Ubuntu, Cantarell, sans-serif;
4
+ font-size: 14px;
5
+ line-height: 1.4;
6
+ z-index: 999999;
7
+ box-sizing: border-box;
8
+ }
9
+
10
+ .feedback-widget *,
11
+ .feedback-widget *::before,
12
+ .feedback-widget *::after {
13
+ box-sizing: border-box;
14
+ }
15
+
16
+ .feedback-widget-button {
17
+ position: fixed;
18
+ z-index: 999999;
19
+ }
20
+
21
+ .feedback-widget-button.position-bottom-right {
22
+ bottom: 20px;
23
+ right: 20px;
24
+ }
25
+
26
+ .feedback-widget-button.position-bottom-left {
27
+ bottom: 20px;
28
+ left: 20px;
29
+ }
30
+
31
+ .feedback-widget-button.position-top-right {
32
+ top: 20px;
33
+ right: 20px;
34
+ }
35
+
36
+ .feedback-widget-button.position-top-left {
37
+ top: 20px;
38
+ left: 20px;
39
+ }
40
+
41
+ .feedback-trigger-btn {
42
+ position: relative;
43
+ display: flex;
44
+ align-items: center;
45
+ justify-content: center;
46
+ gap: 12px;
47
+ height: 44px;
48
+ overflow: hidden;
49
+ border-radius: 0.5rem;
50
+ border: none;
51
+ padding: 10px 16px;
52
+ font-size: 14px;
53
+ font-weight: 500;
54
+ font-family: inherit;
55
+ cursor: pointer;
56
+ transition: all 0.3s duration;
57
+ color: white;
58
+ background: #155EEF;
59
+ box-shadow: 0 1px 2px 0 rgba(16, 24, 40, 0.05);
60
+ }
61
+
62
+ .feedback-trigger-btn:hover:not(:disabled) {
63
+ background: #004EEB;
64
+ box-shadow: 0 1px 2px 0 rgba(16, 24, 40, 0.1);
65
+ }
66
+
67
+ .feedback-trigger-btn:disabled {
68
+ opacity: 0.7;
69
+ cursor: not-allowed;
70
+ }
71
+
72
+ .feedback-trigger-btn:focus-visible {
73
+ outline: 2px solid #155EEF;
74
+ outline-offset: 2px;
75
+ }
76
+
77
+ .feedback-modal {
78
+ position: fixed;
79
+ top: 0;
80
+ left: 0;
81
+ right: 0;
82
+ bottom: 0;
83
+ z-index: 1000000;
84
+ display: flex;
85
+ align-items: center;
86
+ justify-content: center;
87
+ font-family: inherit;
88
+ }
89
+
90
+ .feedback-modal-overlay {
91
+ position: absolute;
92
+ top: 0;
93
+ left: 0;
94
+ right: 0;
95
+ bottom: 0;
96
+ background: rgba(0, 0, 0, 0.5);
97
+ display: flex;
98
+ align-items: center;
99
+ justify-content: center;
100
+ }
101
+
102
+ .feedback-modal-content {
103
+ background: white;
104
+ border-radius: 8px;
105
+ box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
106
+ min-width: 460px;
107
+ max-width: 500px;
108
+ width: 100%;
109
+ padding: 16px;
110
+ max-height: 85vh;
111
+ overflow-y: hidden;
112
+ position: relative;
113
+ }
114
+
115
+ .feedback-modal.theme-dark .feedback-modal-content {
116
+ background: #1F2937;
117
+ color: white;
118
+ }
119
+
120
+ .feedback-modal-header {
121
+ display: flex;
122
+ align-items: center;
123
+ justify-content: space-between;
124
+ padding: 16px;
125
+ border-bottom: 1px solid #D1D5DB;
126
+ flex-shrink: 0;
127
+ }
128
+
129
+ .feedback-modal.theme-dark .feedback-modal-header {
130
+ border-bottom-color: #374151;
131
+ }
132
+
133
+ .feedback-modal-header h3 {
134
+ margin: 0;
135
+ font-size: 16px;
136
+ font-weight: 600;
137
+ }
138
+
139
+ .feedback-modal-close {
140
+ background: none;
141
+ border: none;
142
+ font-size: 24px;
143
+ cursor: pointer;
144
+ color: #6B7280;
145
+ padding: 0;
146
+ width: 24px;
147
+ height: 24px;
148
+ display: flex;
149
+ align-items: center;
150
+ justify-content: center;
151
+ transition: all 0.3s ease;
152
+ }
153
+
154
+ .feedback-modal-close:hover {
155
+ color: #374151;
156
+ }
157
+
158
+ .feedback-modal-close:focus-visible {
159
+ outline: 2px solid #155EEF;
160
+ outline-offset: 2px;
161
+ }
162
+
163
+ .feedback-modal.theme-dark .feedback-modal-close {
164
+ color: #9CA3AF;
165
+ }
166
+
167
+ .feedback-modal.theme-dark .feedback-modal-close:hover {
168
+ color: #D1D5DB;
169
+ }
170
+
171
+ .feedback-form {
172
+ padding: 16px;
173
+ }
174
+
175
+ .feedback-form-group {
176
+ display: flex;
177
+ flex-direction: column;
178
+ gap: 4px;
179
+ margin-bottom: 12px;
180
+ }
181
+
182
+ .feedback-form-group:last-child {
183
+ margin-bottom: 0;
184
+ }
185
+
186
+ .feedback-form-group label {
187
+ font-size: 14px;
188
+ font-weight: 500;
189
+ line-height: 1.25;
190
+ color: #374151;
191
+ }
192
+
193
+ .feedback-modal.theme-dark .feedback-form-group label {
194
+ color: #D1D5DB;
195
+ }
196
+
197
+ .feedback-form-group input {
198
+ height: 40px;
199
+ width: 100%;
200
+ border-radius: 6px;
201
+ border: 1px solid #D1D5DB;
202
+ padding: 2px 12px;
203
+ font-size: 14px;
204
+ font-weight: 400;
205
+ line-height: 1.25;
206
+ color: #1F2937;
207
+ font-family: inherit;
208
+ outline: none;
209
+ transition: all 0.2s ease;
210
+ }
211
+
212
+ .feedback-form-group input::placeholder {
213
+ font-size: 14px;
214
+ color: #6B7280;
215
+ }
216
+
217
+ .feedback-form-group input:focus {
218
+ border-color: #84ADFF;
219
+ box-shadow: 0 0 0 1px rgba(16, 24, 40, 0.05), 0 0 0 3px rgba(41, 112, 255, 0.2);
220
+ }
221
+
222
+ .feedback-form-group input:focus-visible {
223
+ outline: none;
224
+ }
225
+
226
+ .feedback-form-group textarea {
227
+ min-height: 100px;
228
+ width: 100%;
229
+ resize: both;
230
+ border-radius: 6px;
231
+ border: 1px solid #D1D5DB;
232
+ padding: 2px 12px;
233
+ font-size: 14px;
234
+ font-weight: 400;
235
+ line-height: 1.25;
236
+ color: #1F2937;
237
+ font-family: inherit;
238
+ outline: none;
239
+ transition: all 0.2s ease;
240
+ }
241
+
242
+ .feedback-form-group textarea::placeholder {
243
+ font-size: 14px;
244
+ color: #6B7280;
245
+ }
246
+
247
+ .feedback-form-group textarea:focus {
248
+ border-color: #84ADFF;
249
+ box-shadow: 0 0 0 1px rgba(16, 24, 40, 0.05), 0 0 0 3px rgba(41, 112, 255, 0.2);
250
+ }
251
+
252
+ .feedback-form-group textarea:focus-visible {
253
+ outline: none;
254
+ }
255
+
256
+ .feedback-modal.theme-dark .feedback-form-group input,
257
+ .feedback-modal.theme-dark .feedback-form-group textarea {
258
+ background: #374151;
259
+ border-color: #4B5563;
260
+ color: white;
261
+ }
262
+
263
+ .feedback-btn {
264
+ position: relative;
265
+ display: inline-flex;
266
+ align-items: center;
267
+ justify-content: center;
268
+ overflow: hidden;
269
+ border-radius: 6px;
270
+ border: none;
271
+ height: 40px;
272
+ padding: 2px 16px;
273
+ font-size: 14px;
274
+ font-weight: 500;
275
+ font-family: inherit;
276
+ cursor: pointer;
277
+ transition: all 0.2s ease;
278
+ }
279
+
280
+ .feedback-btn:disabled {
281
+ opacity: 0.7;
282
+ cursor: not-allowed;
283
+ }
284
+
285
+ .feedback-btn:focus-visible {
286
+ outline: 2px solid #155EEF;
287
+ outline-offset: 2px;
288
+ }
289
+
290
+ .feedback-btn-submit {
291
+ background: #155EEF;
292
+ color: white;
293
+ }
294
+
295
+ .feedback-btn-submit:hover:not(:disabled) {
296
+ background: #004EEB;
297
+ }
298
+
299
+ .feedback-btn-cancel {
300
+ background: transparent;
301
+ color: #6B7280;
302
+ border: 1px solid #D1D5DB;
303
+ }
304
+
305
+ .feedback-btn-cancel:hover:not(:disabled) {
306
+ background: #F9FAFB;
307
+ border-color: #9CA3AF;
308
+ color: #374151;
309
+ }
310
+
311
+ .feedback-modal.theme-dark .feedback-btn-cancel {
312
+ color: #D1D5DB;
313
+ border-color: #4B5563;
314
+ }
315
+
316
+ .feedback-modal.theme-dark .feedback-btn-cancel:hover:not(:disabled) {
317
+ background: #374151;
318
+ }
319
+
320
+ .feedback-form-actions {
321
+ display: flex;
322
+ gap: 8px;
323
+ justify-content: flex-end;
324
+ margin-top: 16px;
325
+ padding-top: 4px;
326
+ }
327
+
328
+ .feedback-loading {
329
+ width: 20px;
330
+ height: 20px;
331
+ border-radius: 50%;
332
+ mask: radial-gradient(transparent 62%, white 65%);
333
+ -webkit-mask: radial-gradient(transparent 62%, white 65%);
334
+ animation: feedbackRotate 0.7s linear infinite;
335
+ }
336
+
337
+ .feedback-loading-white {
338
+ background: conic-gradient(from 0deg, rgba(255, 255, 255, 0.5), white);
339
+ }
340
+
341
+ .feedback-loading-blue {
342
+ background: conic-gradient(from 0deg, #004EEB, #eff4ff);
343
+ }
344
+
345
+ @keyframes feedbackRotate {
346
+ 0% { transform: rotate(0deg); }
347
+ 100% { transform: rotate(360deg); }
348
+ }
349
+
350
+ .feedback-error {
351
+ color: #F04438;
352
+ font-size: 14px;
353
+ font-weight: 400;
354
+ margin-top: 4px;
355
+ text-transform: capitalize;
356
+ }
357
+
358
+ .feedback-form-error {
359
+ color: #F04438;
360
+ font-size: 14px;
361
+ margin-top: 12px;
362
+ padding: 8px 12px;
363
+ background: #FEE2E2;
364
+ border: 1px solid #FECACA;
365
+ border-radius: 6px;
366
+ }
367
+
368
+ .feedback-modal.theme-dark .feedback-form-error {
369
+ background: #7F1D1D;
370
+ border-color: #991B1B;
371
+ color: #FCA5A5;
372
+ }
373
+
374
+ .feedback-form-group.error input,
375
+ .feedback-form-group.error textarea {
376
+ border-color: #FDA29B;
377
+ }
378
+
379
+ .feedback-form-group.error input:focus,
380
+ .feedback-form-group.error textarea:focus {
381
+ border-color: #FDA29B;
382
+ box-shadow: 0 0 0 1px rgba(16, 24, 40, 0.05), 0 0 0 4px rgba(253, 162, 155, 0.3);
383
+ }
384
+
385
+ .feedback-success-notification {
386
+ position: fixed;
387
+ top: 20px;
388
+ right: 20px;
389
+ z-index: 1000001;
390
+ background: white;
391
+ border: 1px solid #D1FAE5;
392
+ border-radius: 8px;
393
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
394
+ animation: slideInRight 0.3s ease-out;
395
+ }
396
+
397
+ .feedback-success-content {
398
+ display: flex;
399
+ align-items: center;
400
+ padding: 12px 16px;
401
+ gap: 12px;
402
+ }
403
+
404
+ .feedback-success-content span {
405
+ color: #059669;
406
+ font-weight: 500;
407
+ font-size: 14px;
408
+ }
409
+
410
+ .feedback-success-close {
411
+ background: none;
412
+ border: none;
413
+ color: #6B7280;
414
+ cursor: pointer;
415
+ font-size: 18px;
416
+ padding: 0;
417
+ width: 20px;
418
+ height: 20px;
419
+ display: flex;
420
+ align-items: center;
421
+ justify-content: center;
422
+ transition: all 0.3s ease;
423
+ }
424
+
425
+ .feedback-success-close:hover {
426
+ color: #374151;
427
+ }
428
+
429
+ .feedback-success-close:focus-visible {
430
+ outline: 2px solid #155EEF;
431
+ outline-offset: 2px;
432
+ }
433
+
434
+ @keyframes slideInRight {
435
+ from {
436
+ transform: translateX(100%);
437
+ opacity: 0;
438
+ }
439
+ to {
440
+ transform: translateX(0);
441
+ opacity: 1;
442
+ }
443
+ }
444
+
445
+ @keyframes fadeIn {
446
+ from { opacity: 0; }
447
+ to { opacity: 1; }
448
+ }
449
+
450
+ .feedback-modal {
451
+ animation: fadeIn 0.2s ease-out;
452
+ }
453
+
454
+ .feedback-modal-content {
455
+ animation: slideInUp 0.3s ease-out;
456
+ }
457
+
458
+ @keyframes slideInUp {
459
+ from {
460
+ transform: translateY(20px);
461
+ opacity: 0;
462
+ }
463
+ to {
464
+ transform: translateY(0);
465
+ opacity: 1;
466
+ }
467
+ }
468
+
469
+ @media (max-width: 640px) {
470
+ .feedback-modal {
471
+ padding: 8px;
472
+ }
473
+
474
+ .feedback-modal-content {
475
+ min-width: 280px;
476
+ max-width: 100%;
477
+ max-height: 95vh;
478
+ }
479
+
480
+ .feedback-form {
481
+ padding: 16px;
482
+ }
483
+
484
+ .feedback-modal-header {
485
+ padding: 16px;
486
+ }
487
+
488
+ .feedback-modal-header h3 {
489
+ font-size: 15px;
490
+ }
491
+
492
+ .feedback-form-actions {
493
+ flex-direction: column;
494
+ gap: 8px;
495
+ }
496
+
497
+ .feedback-btn {
498
+ width: 100%;
499
+ height: 40px;
500
+ }
501
+
502
+ .feedback-widget-button {
503
+ bottom: 16px;
504
+ right: 16px;
505
+ }
506
+
507
+ .feedback-widget-button.position-bottom-left {
508
+ left: 16px;
509
+ }
510
+
511
+ .feedback-trigger-btn {
512
+ padding: 10px 16px;
513
+ font-size: 13px;
514
+ }
515
+
516
+ .feedback-success-notification {
517
+ top: 8px;
518
+ right: 8px;
519
+ left: 8px;
520
+ max-width: none;
521
+ }
522
+
523
+ .feedback-form-group input {
524
+ height: 40px;
525
+ padding: 2px 12px;
526
+ }
527
+
528
+ .feedback-form-group textarea {
529
+ min-height: 80px;
530
+ padding: 2px 12px;
531
+ }
532
+ }
533
+
534
+ @media (prefers-reduced-motion: reduce) {
535
+ .feedback-trigger-btn,
536
+ .feedback-btn,
537
+ .feedback-modal,
538
+ .feedback-modal-content,
539
+ .feedback-success-notification,
540
+ .feedback-loading {
541
+ transition: none;
542
+ animation: none;
543
+ }
544
+
545
+ .feedback-trigger-btn:hover {
546
+ transform: none;
547
+ }
548
+ }
549
+
550
+ @media print {
551
+ .feedback-widget,
552
+ .feedback-modal,
553
+ .feedback-success-notification {
554
+ display: none !important;
555
+ }
556
+ }
557
+ `;
@@ -0,0 +1,12 @@
1
+ declare module '@product7/feedback-sdk' {
2
+ export class FeedbackSDK {
3
+ constructor(config: any);
4
+ init(): Promise<any>;
5
+ createWidget(type: string, options?: any): any;
6
+ }
7
+
8
+ export class ButtonWidget {
9
+ mount(container?: string | HTMLElement): this;
10
+ destroy(): void;
11
+ }
12
+ }
@@ -0,0 +1,142 @@
1
+ export class SDKError extends Error {
2
+ constructor(message, cause) {
3
+ super(message);
4
+ this.name = 'SDKError';
5
+ this.cause = cause;
6
+
7
+ if (Error.captureStackTrace) {
8
+ Error.captureStackTrace(this, SDKError);
9
+ }
10
+ }
11
+ }
12
+
13
+ export class APIError extends Error {
14
+ constructor(status, message, response) {
15
+ super(message);
16
+ this.name = 'APIError';
17
+ this.status = status;
18
+ this.response = response;
19
+
20
+ if (Error.captureStackTrace) {
21
+ Error.captureStackTrace(this, APIError);
22
+ }
23
+ }
24
+
25
+ isNetworkError() {
26
+ return this.status === 0;
27
+ }
28
+
29
+ isClientError() {
30
+ return this.status >= 400 && this.status < 500;
31
+ }
32
+
33
+ isServerError() {
34
+ return this.status >= 500 && this.status < 600;
35
+ }
36
+ }
37
+
38
+ export class WidgetError extends Error {
39
+ constructor(message, widgetType, widgetId) {
40
+ super(message);
41
+ this.name = 'WidgetError';
42
+ this.widgetType = widgetType;
43
+ this.widgetId = widgetId;
44
+
45
+ if (Error.captureStackTrace) {
46
+ Error.captureStackTrace(this, WidgetError);
47
+ }
48
+ }
49
+ }
50
+
51
+ export class ConfigError extends Error {
52
+ constructor(message, configKey) {
53
+ super(message);
54
+ this.name = 'ConfigError';
55
+ this.configKey = configKey;
56
+
57
+ if (Error.captureStackTrace) {
58
+ Error.captureStackTrace(this, ConfigError);
59
+ }
60
+ }
61
+ }
62
+
63
+ export class ValidationError extends Error {
64
+ constructor(message, field, value) {
65
+ super(message);
66
+ this.name = 'ValidationError';
67
+ this.field = field;
68
+ this.value = value;
69
+
70
+ if (Error.captureStackTrace) {
71
+ Error.captureStackTrace(this, ValidationError);
72
+ }
73
+ }
74
+ }
75
+
76
+ export class ErrorHandler {
77
+ constructor(debug = false) {
78
+ this.debug = debug;
79
+ }
80
+
81
+ handle(error, context = '') {
82
+ const errorInfo = {
83
+ name: error.name,
84
+ message: error.message,
85
+ context,
86
+ timestamp: new Date().toISOString(),
87
+ };
88
+
89
+ if (error instanceof APIError) {
90
+ errorInfo.status = error.status;
91
+ errorInfo.type = 'api';
92
+ } else if (error instanceof WidgetError) {
93
+ errorInfo.widgetType = error.widgetType;
94
+ errorInfo.widgetId = error.widgetId;
95
+ errorInfo.type = 'widget';
96
+ } else if (error instanceof ConfigError) {
97
+ errorInfo.configKey = error.configKey;
98
+ errorInfo.type = 'config';
99
+ } else if (error instanceof ValidationError) {
100
+ errorInfo.field = error.field;
101
+ errorInfo.value = error.value;
102
+ errorInfo.type = 'validation';
103
+ } else {
104
+ errorInfo.type = 'unknown';
105
+ }
106
+
107
+ if (this.debug) {
108
+ console.error('[FeedbackSDK Error]', errorInfo, error);
109
+ } else {
110
+ console.error('[FeedbackSDK Error]', error.message);
111
+ }
112
+
113
+ return errorInfo;
114
+ }
115
+
116
+ getUserMessage(error) {
117
+ if (error instanceof APIError) {
118
+ if (error.isNetworkError()) {
119
+ return 'Network error. Please check your connection and try again.';
120
+ } else if (error.isClientError()) {
121
+ return 'Invalid request. Please check your input and try again.';
122
+ } else if (error.isServerError()) {
123
+ return 'Server error. Please try again later.';
124
+ }
125
+ return 'Failed to submit feedback. Please try again.';
126
+ }
127
+
128
+ if (error instanceof ValidationError) {
129
+ return `Please check your ${error.field}: ${error.message}`;
130
+ }
131
+
132
+ if (error instanceof ConfigError) {
133
+ return 'Configuration error. Please check your SDK setup.';
134
+ }
135
+
136
+ if (error instanceof WidgetError) {
137
+ return 'Widget error. Please try refreshing the page.';
138
+ }
139
+
140
+ return 'An unexpected error occurred. Please try again.';
141
+ }
142
+ }