vanilla-agent 1.22.0 → 1.24.0
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/index.cjs +26 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +291 -69
- package/dist/index.d.ts +291 -69
- package/dist/index.global.js +48 -44
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +26 -22
- package/dist/index.js.map +1 -1
- package/dist/widget.css +224 -0
- package/package.json +2 -2
- package/src/client.ts +180 -5
- package/src/components/feedback.ts +377 -0
- package/src/index.ts +13 -1
- package/src/session.ts +55 -3
- package/src/styles/widget.css +224 -0
- package/src/types.ts +58 -6
- package/src/ui.ts +95 -1
- package/src/utils/code-generators.ts +237 -0
- package/src/utils/message-id.ts +35 -0
package/src/styles/widget.css
CHANGED
|
@@ -1400,3 +1400,227 @@ form:focus-within textarea {
|
|
|
1400
1400
|
height: 14px;
|
|
1401
1401
|
flex-shrink: 0;
|
|
1402
1402
|
}
|
|
1403
|
+
|
|
1404
|
+
/* ============================================================================
|
|
1405
|
+
* Feedback UI Components (CSAT/NPS)
|
|
1406
|
+
* ============================================================================ */
|
|
1407
|
+
|
|
1408
|
+
.tvw-feedback-container {
|
|
1409
|
+
background: var(--cw-surface, #ffffff);
|
|
1410
|
+
border: 1px solid var(--cw-border, #e5e7eb);
|
|
1411
|
+
border-radius: var(--tvw-cw-radius-lg, 12px);
|
|
1412
|
+
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
|
|
1413
|
+
padding: 1.25rem;
|
|
1414
|
+
max-width: 100%;
|
|
1415
|
+
margin: 0.75rem;
|
|
1416
|
+
animation: tvw-feedback-fade-in 0.3s ease-out;
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
@keyframes tvw-feedback-fade-in {
|
|
1420
|
+
from {
|
|
1421
|
+
opacity: 0;
|
|
1422
|
+
transform: translateY(10px);
|
|
1423
|
+
}
|
|
1424
|
+
to {
|
|
1425
|
+
opacity: 1;
|
|
1426
|
+
transform: translateY(0);
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
.tvw-feedback-content {
|
|
1431
|
+
display: flex;
|
|
1432
|
+
flex-direction: column;
|
|
1433
|
+
gap: 1rem;
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
.tvw-feedback-header {
|
|
1437
|
+
text-align: center;
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
.tvw-feedback-title {
|
|
1441
|
+
margin: 0 0 0.25rem 0;
|
|
1442
|
+
font-size: 1rem;
|
|
1443
|
+
font-weight: 600;
|
|
1444
|
+
color: var(--cw-primary, #111827);
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
.tvw-feedback-subtitle {
|
|
1448
|
+
margin: 0;
|
|
1449
|
+
font-size: 0.875rem;
|
|
1450
|
+
color: var(--cw-muted, #6b7280);
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
/* CSAT Star Rating */
|
|
1454
|
+
.tvw-feedback-rating-csat {
|
|
1455
|
+
display: flex;
|
|
1456
|
+
justify-content: center;
|
|
1457
|
+
gap: 0.5rem;
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
.tvw-feedback-star-btn {
|
|
1461
|
+
background: transparent;
|
|
1462
|
+
border: none;
|
|
1463
|
+
cursor: pointer;
|
|
1464
|
+
padding: 0.25rem;
|
|
1465
|
+
color: var(--cw-border, #d1d5db);
|
|
1466
|
+
transition: color 0.2s ease, transform 0.15s ease;
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
.tvw-feedback-star-btn:hover {
|
|
1470
|
+
transform: scale(1.15);
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
.tvw-feedback-star-btn.selected {
|
|
1474
|
+
color: #fbbf24;
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
.tvw-feedback-star-btn .tvw-feedback-star {
|
|
1478
|
+
width: 32px;
|
|
1479
|
+
height: 32px;
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
.tvw-feedback-star-btn.selected .tvw-feedback-star {
|
|
1483
|
+
fill: #fbbf24;
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
/* NPS Number Rating */
|
|
1487
|
+
.tvw-feedback-rating-nps {
|
|
1488
|
+
display: flex;
|
|
1489
|
+
flex-direction: column;
|
|
1490
|
+
gap: 0.5rem;
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
.tvw-feedback-labels {
|
|
1494
|
+
display: flex;
|
|
1495
|
+
justify-content: space-between;
|
|
1496
|
+
font-size: 0.75rem;
|
|
1497
|
+
color: var(--cw-muted, #6b7280);
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
.tvw-feedback-numbers {
|
|
1501
|
+
display: flex;
|
|
1502
|
+
gap: 0.25rem;
|
|
1503
|
+
justify-content: center;
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
.tvw-feedback-number-btn {
|
|
1507
|
+
width: 28px;
|
|
1508
|
+
height: 28px;
|
|
1509
|
+
display: flex;
|
|
1510
|
+
align-items: center;
|
|
1511
|
+
justify-content: center;
|
|
1512
|
+
font-size: 0.75rem;
|
|
1513
|
+
font-weight: 500;
|
|
1514
|
+
border-radius: var(--tvw-cw-radius-sm, 6px);
|
|
1515
|
+
border: 1px solid var(--cw-border, #e5e7eb);
|
|
1516
|
+
background: var(--cw-surface, #ffffff);
|
|
1517
|
+
color: var(--cw-primary, #111827);
|
|
1518
|
+
cursor: pointer;
|
|
1519
|
+
transition: all 0.2s ease;
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
.tvw-feedback-number-btn:hover {
|
|
1523
|
+
border-color: var(--cw-accent, #1d4ed8);
|
|
1524
|
+
background: var(--cw-container, #f3f4f6);
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
.tvw-feedback-number-btn.selected {
|
|
1528
|
+
color: #ffffff;
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
/* NPS Color coding */
|
|
1532
|
+
.tvw-feedback-number-btn.tvw-feedback-detractor.selected {
|
|
1533
|
+
background: #ef4444;
|
|
1534
|
+
border-color: #ef4444;
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
.tvw-feedback-number-btn.tvw-feedback-passive.selected {
|
|
1538
|
+
background: #f59e0b;
|
|
1539
|
+
border-color: #f59e0b;
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
.tvw-feedback-number-btn.tvw-feedback-promoter.selected {
|
|
1543
|
+
background: #22c55e;
|
|
1544
|
+
border-color: #22c55e;
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
/* Comment textarea */
|
|
1548
|
+
.tvw-feedback-comment-container {
|
|
1549
|
+
width: 100%;
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
.tvw-feedback-comment {
|
|
1553
|
+
width: 100%;
|
|
1554
|
+
padding: 0.625rem;
|
|
1555
|
+
font-size: 0.875rem;
|
|
1556
|
+
font-family: inherit;
|
|
1557
|
+
border: 1px solid var(--cw-border, #e5e7eb);
|
|
1558
|
+
border-radius: var(--tvw-cw-radius-sm, 6px);
|
|
1559
|
+
background: var(--cw-surface, #ffffff);
|
|
1560
|
+
color: var(--cw-primary, #111827);
|
|
1561
|
+
resize: vertical;
|
|
1562
|
+
box-sizing: border-box;
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
.tvw-feedback-comment:focus {
|
|
1566
|
+
outline: none;
|
|
1567
|
+
border-color: var(--cw-accent, #1d4ed8);
|
|
1568
|
+
box-shadow: 0 0 0 2px rgba(29, 78, 216, 0.15);
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
.tvw-feedback-comment::placeholder {
|
|
1572
|
+
color: var(--cw-muted, #9ca3af);
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
/* Action buttons */
|
|
1576
|
+
.tvw-feedback-actions {
|
|
1577
|
+
display: flex;
|
|
1578
|
+
gap: 0.5rem;
|
|
1579
|
+
justify-content: flex-end;
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
.tvw-feedback-btn {
|
|
1583
|
+
padding: 0.5rem 1rem;
|
|
1584
|
+
font-size: 0.875rem;
|
|
1585
|
+
font-weight: 500;
|
|
1586
|
+
border-radius: var(--tvw-cw-radius-sm, 6px);
|
|
1587
|
+
cursor: pointer;
|
|
1588
|
+
transition: all 0.2s ease;
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
.tvw-feedback-btn-skip {
|
|
1592
|
+
background: transparent;
|
|
1593
|
+
border: 1px solid var(--cw-border, #e5e7eb);
|
|
1594
|
+
color: var(--cw-muted, #6b7280);
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
.tvw-feedback-btn-skip:hover {
|
|
1598
|
+
background: var(--cw-container, #f3f4f6);
|
|
1599
|
+
color: var(--cw-primary, #111827);
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
.tvw-feedback-btn-submit {
|
|
1603
|
+
background: var(--cw-accent, #1d4ed8);
|
|
1604
|
+
border: 1px solid var(--cw-accent, #1d4ed8);
|
|
1605
|
+
color: #ffffff;
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
.tvw-feedback-btn-submit:hover:not(:disabled) {
|
|
1609
|
+
opacity: 0.9;
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
.tvw-feedback-btn-submit:disabled {
|
|
1613
|
+
opacity: 0.6;
|
|
1614
|
+
cursor: not-allowed;
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
/* Shake animation for validation */
|
|
1618
|
+
@keyframes tvw-feedback-shake {
|
|
1619
|
+
0%, 100% { transform: translateX(0); }
|
|
1620
|
+
10%, 30%, 50%, 70%, 90% { transform: translateX(-4px); }
|
|
1621
|
+
20%, 40%, 60%, 80% { transform: translateX(4px); }
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
.tvw-feedback-shake {
|
|
1625
|
+
animation: tvw-feedback-shake 0.5s ease-in-out;
|
|
1626
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -22,6 +22,7 @@ export type AgentWidgetRequestPayload = {
|
|
|
22
22
|
messages: AgentWidgetRequestPayloadMessage[];
|
|
23
23
|
flowId?: string;
|
|
24
24
|
context?: Record<string, unknown>;
|
|
25
|
+
metadata?: Record<string, unknown>;
|
|
25
26
|
};
|
|
26
27
|
|
|
27
28
|
export type AgentWidgetRequestMiddlewareContext = {
|
|
@@ -104,6 +105,23 @@ export type AgentWidgetMessageFeedback = {
|
|
|
104
105
|
|
|
105
106
|
/**
|
|
106
107
|
* Configuration for message action buttons (copy, upvote, downvote)
|
|
108
|
+
*
|
|
109
|
+
* **Client Token Mode**: When using `clientToken`, feedback is automatically
|
|
110
|
+
* sent to your Travrse backend. Just enable the buttons and you're done!
|
|
111
|
+
* The `onFeedback` and `onCopy` callbacks are optional for additional local handling.
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```typescript
|
|
115
|
+
* // With clientToken - feedback is automatic!
|
|
116
|
+
* config: {
|
|
117
|
+
* clientToken: 'ct_live_...',
|
|
118
|
+
* messageActions: {
|
|
119
|
+
* showUpvote: true,
|
|
120
|
+
* showDownvote: true,
|
|
121
|
+
* // No onFeedback needed - sent to backend automatically
|
|
122
|
+
* }
|
|
123
|
+
* }
|
|
124
|
+
* ```
|
|
107
125
|
*/
|
|
108
126
|
export type AgentWidgetMessageActionsConfig = {
|
|
109
127
|
/**
|
|
@@ -117,13 +135,15 @@ export type AgentWidgetMessageActionsConfig = {
|
|
|
117
135
|
*/
|
|
118
136
|
showCopy?: boolean;
|
|
119
137
|
/**
|
|
120
|
-
* Show upvote button
|
|
121
|
-
*
|
|
138
|
+
* Show upvote button.
|
|
139
|
+
* When using `clientToken`, feedback is sent to the backend automatically.
|
|
140
|
+
* @default false
|
|
122
141
|
*/
|
|
123
142
|
showUpvote?: boolean;
|
|
124
143
|
/**
|
|
125
|
-
* Show downvote button
|
|
126
|
-
*
|
|
144
|
+
* Show downvote button.
|
|
145
|
+
* When using `clientToken`, feedback is sent to the backend automatically.
|
|
146
|
+
* @default false
|
|
127
147
|
*/
|
|
128
148
|
showDownvote?: boolean;
|
|
129
149
|
/**
|
|
@@ -144,11 +164,19 @@ export type AgentWidgetMessageActionsConfig = {
|
|
|
144
164
|
*/
|
|
145
165
|
layout?: "pill-inside" | "row-inside";
|
|
146
166
|
/**
|
|
147
|
-
* Callback when user submits feedback (upvote/downvote)
|
|
167
|
+
* Callback when user submits feedback (upvote/downvote).
|
|
168
|
+
*
|
|
169
|
+
* **Note**: When using `clientToken`, feedback is AUTOMATICALLY sent to your
|
|
170
|
+
* backend via `/v1/client/feedback`. This callback is called IN ADDITION to
|
|
171
|
+
* the automatic submission, useful for updating local UI or analytics.
|
|
148
172
|
*/
|
|
149
173
|
onFeedback?: (feedback: AgentWidgetMessageFeedback) => void;
|
|
150
174
|
/**
|
|
151
|
-
* Callback when user copies a message
|
|
175
|
+
* Callback when user copies a message.
|
|
176
|
+
*
|
|
177
|
+
* **Note**: When using `clientToken`, copy events are AUTOMATICALLY tracked
|
|
178
|
+
* via `/v1/client/feedback`. This callback is called IN ADDITION to the
|
|
179
|
+
* automatic tracking.
|
|
152
180
|
*/
|
|
153
181
|
onCopy?: (message: AgentWidgetMessage) => void;
|
|
154
182
|
};
|
|
@@ -567,9 +595,33 @@ export type ClientInitResponse = {
|
|
|
567
595
|
export type ClientChatRequest = {
|
|
568
596
|
session_id: string;
|
|
569
597
|
messages: Array<{
|
|
598
|
+
id?: string;
|
|
570
599
|
role: 'user' | 'assistant' | 'system';
|
|
571
600
|
content: string;
|
|
572
601
|
}>;
|
|
602
|
+
/** ID for the expected assistant response message */
|
|
603
|
+
assistant_message_id?: string;
|
|
604
|
+
metadata?: Record<string, unknown>;
|
|
605
|
+
context?: Record<string, unknown>;
|
|
606
|
+
};
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* Feedback types supported by the API
|
|
610
|
+
*/
|
|
611
|
+
export type ClientFeedbackType = 'upvote' | 'downvote' | 'copy' | 'csat' | 'nps';
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* Request payload for /v1/client/feedback endpoint
|
|
615
|
+
*/
|
|
616
|
+
export type ClientFeedbackRequest = {
|
|
617
|
+
session_id: string;
|
|
618
|
+
/** Required for upvote, downvote, copy feedback types */
|
|
619
|
+
message_id?: string;
|
|
620
|
+
type: ClientFeedbackType;
|
|
621
|
+
/** Required for csat (1-5) and nps (0-10) feedback types */
|
|
622
|
+
rating?: number;
|
|
623
|
+
/** Optional comment for any feedback type */
|
|
624
|
+
comment?: string;
|
|
573
625
|
};
|
|
574
626
|
|
|
575
627
|
// ============================================================================
|
package/src/ui.ts
CHANGED
|
@@ -43,6 +43,12 @@ import {
|
|
|
43
43
|
extractComponentDirectiveFromMessage,
|
|
44
44
|
hasComponentDirective
|
|
45
45
|
} from "./utils/component-middleware";
|
|
46
|
+
import {
|
|
47
|
+
createCSATFeedback,
|
|
48
|
+
createNPSFeedback,
|
|
49
|
+
type CSATFeedbackOptions,
|
|
50
|
+
type NPSFeedbackOptions
|
|
51
|
+
} from "./components/feedback";
|
|
46
52
|
|
|
47
53
|
// Default localStorage key for chat history (automatically cleared on clear chat)
|
|
48
54
|
const DEFAULT_CHAT_HISTORY_STORAGE_KEY = "vanilla-agent-chat-history";
|
|
@@ -91,6 +97,11 @@ type Controller = {
|
|
|
91
97
|
isOpen: () => boolean;
|
|
92
98
|
isVoiceActive: () => boolean;
|
|
93
99
|
getState: () => AgentWidgetStateSnapshot;
|
|
100
|
+
// Feedback methods (CSAT/NPS)
|
|
101
|
+
showCSATFeedback: (options?: Partial<CSATFeedbackOptions>) => void;
|
|
102
|
+
showNPSFeedback: (options?: Partial<NPSFeedbackOptions>) => void;
|
|
103
|
+
submitCSATFeedback: (rating: number, comment?: string) => Promise<void>;
|
|
104
|
+
submitNPSFeedback: (rating: number, comment?: string) => Promise<void>;
|
|
94
105
|
};
|
|
95
106
|
|
|
96
107
|
const buildPostprocessor = (
|
|
@@ -229,13 +240,35 @@ export const createAgentExperience = (
|
|
|
229
240
|
let showReasoning = config.features?.showReasoning ?? true;
|
|
230
241
|
let showToolCalls = config.features?.showToolCalls ?? true;
|
|
231
242
|
|
|
232
|
-
// Create message action callbacks that emit events
|
|
243
|
+
// Create message action callbacks that emit events and optionally send to API
|
|
233
244
|
const messageActionCallbacks: MessageActionCallbacks = {
|
|
234
245
|
onCopy: (message: AgentWidgetMessage) => {
|
|
235
246
|
eventBus.emit("message:copy", message);
|
|
247
|
+
// Send copy feedback to API if in client token mode
|
|
248
|
+
if (session?.isClientTokenMode()) {
|
|
249
|
+
session.submitMessageFeedback(message.id, 'copy').catch((error) => {
|
|
250
|
+
if (config.debug) {
|
|
251
|
+
// eslint-disable-next-line no-console
|
|
252
|
+
console.error("[AgentWidget] Failed to submit copy feedback:", error);
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
// Call user-provided callback
|
|
257
|
+
config.messageActions?.onCopy?.(message);
|
|
236
258
|
},
|
|
237
259
|
onFeedback: (feedback: AgentWidgetMessageFeedback) => {
|
|
238
260
|
eventBus.emit("message:feedback", feedback);
|
|
261
|
+
// Send feedback to API if in client token mode
|
|
262
|
+
if (session?.isClientTokenMode()) {
|
|
263
|
+
session.submitMessageFeedback(feedback.messageId, feedback.type).catch((error) => {
|
|
264
|
+
if (config.debug) {
|
|
265
|
+
// eslint-disable-next-line no-console
|
|
266
|
+
console.error("[AgentWidget] Failed to submit feedback:", error);
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
// Call user-provided callback
|
|
271
|
+
config.messageActions?.onFeedback?.(feedback);
|
|
239
272
|
}
|
|
240
273
|
};
|
|
241
274
|
|
|
@@ -2797,6 +2830,67 @@ export const createAgentExperience = (
|
|
|
2797
2830
|
streaming: session.isStreaming()
|
|
2798
2831
|
};
|
|
2799
2832
|
},
|
|
2833
|
+
// Feedback methods (CSAT/NPS)
|
|
2834
|
+
showCSATFeedback(options?: Partial<CSATFeedbackOptions>) {
|
|
2835
|
+
// Auto-open widget if closed and launcher is enabled
|
|
2836
|
+
if (!open && launcherEnabled) {
|
|
2837
|
+
setOpenState(true, "system");
|
|
2838
|
+
}
|
|
2839
|
+
|
|
2840
|
+
// Remove any existing feedback forms
|
|
2841
|
+
const existingFeedback = messagesWrapper.querySelector('.tvw-feedback-container');
|
|
2842
|
+
if (existingFeedback) {
|
|
2843
|
+
existingFeedback.remove();
|
|
2844
|
+
}
|
|
2845
|
+
|
|
2846
|
+
const feedbackEl = createCSATFeedback({
|
|
2847
|
+
onSubmit: async (rating, comment) => {
|
|
2848
|
+
if (session.isClientTokenMode()) {
|
|
2849
|
+
await session.submitCSATFeedback(rating, comment);
|
|
2850
|
+
}
|
|
2851
|
+
options?.onSubmit?.(rating, comment);
|
|
2852
|
+
},
|
|
2853
|
+
onDismiss: options?.onDismiss,
|
|
2854
|
+
...options,
|
|
2855
|
+
});
|
|
2856
|
+
|
|
2857
|
+
// Append to messages area at the bottom
|
|
2858
|
+
messagesWrapper.appendChild(feedbackEl);
|
|
2859
|
+
feedbackEl.scrollIntoView({ behavior: 'smooth', block: 'end' });
|
|
2860
|
+
},
|
|
2861
|
+
showNPSFeedback(options?: Partial<NPSFeedbackOptions>) {
|
|
2862
|
+
// Auto-open widget if closed and launcher is enabled
|
|
2863
|
+
if (!open && launcherEnabled) {
|
|
2864
|
+
setOpenState(true, "system");
|
|
2865
|
+
}
|
|
2866
|
+
|
|
2867
|
+
// Remove any existing feedback forms
|
|
2868
|
+
const existingFeedback = messagesWrapper.querySelector('.tvw-feedback-container');
|
|
2869
|
+
if (existingFeedback) {
|
|
2870
|
+
existingFeedback.remove();
|
|
2871
|
+
}
|
|
2872
|
+
|
|
2873
|
+
const feedbackEl = createNPSFeedback({
|
|
2874
|
+
onSubmit: async (rating, comment) => {
|
|
2875
|
+
if (session.isClientTokenMode()) {
|
|
2876
|
+
await session.submitNPSFeedback(rating, comment);
|
|
2877
|
+
}
|
|
2878
|
+
options?.onSubmit?.(rating, comment);
|
|
2879
|
+
},
|
|
2880
|
+
onDismiss: options?.onDismiss,
|
|
2881
|
+
...options,
|
|
2882
|
+
});
|
|
2883
|
+
|
|
2884
|
+
// Append to messages area at the bottom
|
|
2885
|
+
messagesWrapper.appendChild(feedbackEl);
|
|
2886
|
+
feedbackEl.scrollIntoView({ behavior: 'smooth', block: 'end' });
|
|
2887
|
+
},
|
|
2888
|
+
async submitCSATFeedback(rating: number, comment?: string): Promise<void> {
|
|
2889
|
+
return session.submitCSATFeedback(rating, comment);
|
|
2890
|
+
},
|
|
2891
|
+
async submitNPSFeedback(rating: number, comment?: string): Promise<void> {
|
|
2892
|
+
return session.submitNPSFeedback(rating, comment);
|
|
2893
|
+
},
|
|
2800
2894
|
destroy() {
|
|
2801
2895
|
destroyCallbacks.forEach((cb) => cb());
|
|
2802
2896
|
wrapper.remove();
|