@exotel-npm-dev/exotel-ai-assist 1.2.2 → 1.2.4

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.
@@ -1,7 +1,8 @@
1
1
  import { Suggestion, BotConfig } from "../../types";
2
2
  import "../../styles/index.css";
3
- export declare function SuggestionsTab({ suggestions, connected, botConfig, }: {
3
+ export declare function SuggestionsTab({ suggestions, connected, botConfig, sendSuggestionFeedback, }: {
4
4
  suggestions: Suggestion[];
5
5
  connected: boolean;
6
6
  botConfig: BotConfig | null;
7
+ sendSuggestionFeedback: (sequence: number, feedbackType: "good" | "bad" | null, badFeedbackReason?: string | null) => boolean;
7
8
  }): JSX.Element;
@@ -19,6 +19,7 @@ export declare class ExotelAIAssistController extends EventEmitter<ControllerEve
19
19
  destroy(): void;
20
20
  getStatus(): ConnectionStatus;
21
21
  getStreamState(): StreamState | null;
22
+ sendSuggestionFeedback(sequence: number, feedbackType: "good" | "bad" | null, badFeedbackReason?: string | null): boolean;
22
23
  private _ensureTransport;
23
24
  private _handleTransportMessage;
24
25
  private _scheduleReconnect;
@@ -1667,6 +1667,25 @@ class ExotelAIAssistController extends EventEmitter {
1667
1667
  getStreamState() {
1668
1668
  return this._streamState;
1669
1669
  }
1670
+ sendSuggestionFeedback(sequence, feedbackType, badFeedbackReason = null) {
1671
+ if (!this.transport || this.status !== "connected") {
1672
+ console.warn("[ExotelAIAssist] Cannot send feedback: not connected");
1673
+ return false;
1674
+ }
1675
+ const message = {
1676
+ type: "suggestion_feedback",
1677
+ sequence,
1678
+ feedback_type: feedbackType,
1679
+ bad_feedback_reason: badFeedbackReason
1680
+ };
1681
+ try {
1682
+ this.transport.send(JSON.stringify(message));
1683
+ return true;
1684
+ } catch (error) {
1685
+ console.error("[ExotelAIAssist] Failed to send feedback:", error);
1686
+ return false;
1687
+ }
1688
+ }
1670
1689
  _ensureTransport() {
1671
1690
  if (this.transport) return;
1672
1691
  this.transport = createTransport(Utils.hash(this.params));
@@ -1789,17 +1808,27 @@ class ExotelAIAssistController extends EventEmitter {
1789
1808
  });
1790
1809
  this.emit("transcript", lines);
1791
1810
  }
1792
- if (event.event_type === "suggestion" && event.value) {
1793
- const suggestion = {
1794
- id: Utils.getUniqueId(),
1795
- value: event.value,
1796
- timestamp: now
1797
- };
1798
- this.emit("suggestion", suggestion);
1811
+ if (event.event_type === "suggestion") {
1812
+ const val = event.value;
1813
+ const text = typeof val === "object" && val !== null && "text" in val ? val.text : typeof val === "string" ? val : event.text;
1814
+ const sequence = typeof val === "object" && val !== null && "sequence" in val ? val.sequence : 0;
1815
+ if (text) {
1816
+ const suggestion = {
1817
+ id: Utils.getUniqueId(),
1818
+ text,
1819
+ value: text,
1820
+ timestamp: now,
1821
+ sequence,
1822
+ feedbackType: null,
1823
+ badFeedbackReason: null
1824
+ };
1825
+ this.emit("suggestion", suggestion);
1826
+ }
1799
1827
  }
1800
1828
  if (event.event_type === "sentiment" && event.value) {
1829
+ const sentimentValue = typeof event.value === "string" ? event.value : event.value.text;
1801
1830
  const sentiment = {
1802
- label: event.value.toLowerCase(),
1831
+ label: sentimentValue.toLowerCase(),
1803
1832
  timestamp: now
1804
1833
  };
1805
1834
  this.emit("sentiment", sentiment);
@@ -1811,7 +1840,7 @@ class ExotelAIAssistController extends EventEmitter {
1811
1840
  this.emit("streamState", state);
1812
1841
  if (state === "connected" || state === "throttled") {
1813
1842
  this._fireReady();
1814
- } else if (state === "disconnected" && this.readyFired) {
1843
+ } else if ((state === "connection_timeout" || state === "disconnected") && this.readyFired) {
1815
1844
  this.readyFired = false;
1816
1845
  this.emit("onReady", false);
1817
1846
  }
@@ -25,6 +25,7 @@ export interface UseExotelAIAssistReturn {
25
25
  connect: () => void;
26
26
  disconnect: () => void;
27
27
  setParams: (patch: Partial<ExotelAIAssistParams>) => void;
28
+ sendSuggestionFeedback: (sequence: number, feedbackType: "good" | "bad" | null, badFeedbackReason?: string | null) => boolean;
28
29
  }
29
30
  /** Internal extended return — adds botConfig for the UI component only. */
30
31
  export interface UseExotelAIAssistInternalReturn extends UseExotelAIAssistReturn {
package/dist/index.js CHANGED
@@ -19294,6 +19294,30 @@ const Smile = createLucideIcon$1("Smile", [["circle", {
19294
19294
  key: "1p4y9e"
19295
19295
  }]]);
19296
19296
 
19297
+ /**
19298
+ * lucide-react v0.0.1 - ISC
19299
+ */
19300
+
19301
+ const ThumbsDown = createLucideIcon$1("ThumbsDown", [["path", {
19302
+ d: "M17 14V2",
19303
+ key: "8ymqnk"
19304
+ }], ["path", {
19305
+ d: "M9 18.12 10 14H4.17a2 2 0 0 1-1.92-2.56l2.33-8A2 2 0 0 1 6.5 2H20a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.76a2 2 0 0 0-1.79 1.11L12 22h0a3.13 3.13 0 0 1-3-3.88Z",
19306
+ key: "s6e0r"
19307
+ }]]);
19308
+
19309
+ /**
19310
+ * lucide-react v0.0.1 - ISC
19311
+ */
19312
+
19313
+ const ThumbsUp = createLucideIcon$1("ThumbsUp", [["path", {
19314
+ d: "M7 10v12",
19315
+ key: "1qc93n"
19316
+ }], ["path", {
19317
+ d: "M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2h0a3.13 3.13 0 0 1 3 3.88Z",
19318
+ key: "y3tblf"
19319
+ }]]);
19320
+
19297
19321
  const MAX_VISIBLE_TOASTS = 5;
19298
19322
  const AUTO_DISMISS_MS = 3000;
19299
19323
  const ToastContext = reactExports.createContext(null);
@@ -21085,6 +21109,25 @@ class ExotelAIAssistController extends EventEmitter {
21085
21109
  getStreamState() {
21086
21110
  return this._streamState;
21087
21111
  }
21112
+ sendSuggestionFeedback(sequence, feedbackType, badFeedbackReason = null) {
21113
+ if (!this.transport || this.status !== "connected") {
21114
+ console.warn("[ExotelAIAssist] Cannot send feedback: not connected");
21115
+ return false;
21116
+ }
21117
+ const message = {
21118
+ type: "suggestion_feedback",
21119
+ sequence,
21120
+ feedback_type: feedbackType,
21121
+ bad_feedback_reason: badFeedbackReason
21122
+ };
21123
+ try {
21124
+ this.transport.send(JSON.stringify(message));
21125
+ return true;
21126
+ } catch (error) {
21127
+ console.error("[ExotelAIAssist] Failed to send feedback:", error);
21128
+ return false;
21129
+ }
21130
+ }
21088
21131
  _ensureTransport() {
21089
21132
  if (this.transport) return;
21090
21133
  this.transport = createTransport(Utils.hash(this.params));
@@ -21207,17 +21250,27 @@ class ExotelAIAssistController extends EventEmitter {
21207
21250
  });
21208
21251
  this.emit("transcript", lines);
21209
21252
  }
21210
- if (event.event_type === "suggestion" && event.value) {
21211
- const suggestion = {
21212
- id: Utils.getUniqueId(),
21213
- value: event.value,
21214
- timestamp: now
21215
- };
21216
- this.emit("suggestion", suggestion);
21253
+ if (event.event_type === "suggestion") {
21254
+ const val = event.value;
21255
+ const text = typeof val === "object" && val !== null && "text" in val ? val.text : typeof val === "string" ? val : event.text;
21256
+ const sequence = typeof val === "object" && val !== null && "sequence" in val ? val.sequence : 0;
21257
+ if (text) {
21258
+ const suggestion = {
21259
+ id: Utils.getUniqueId(),
21260
+ text,
21261
+ value: text,
21262
+ timestamp: now,
21263
+ sequence,
21264
+ feedbackType: null,
21265
+ badFeedbackReason: null
21266
+ };
21267
+ this.emit("suggestion", suggestion);
21268
+ }
21217
21269
  }
21218
21270
  if (event.event_type === "sentiment" && event.value) {
21271
+ const sentimentValue = typeof event.value === "string" ? event.value : event.value.text;
21219
21272
  const sentiment = {
21220
- label: event.value.toLowerCase(),
21273
+ label: sentimentValue.toLowerCase(),
21221
21274
  timestamp: now
21222
21275
  };
21223
21276
  this.emit("sentiment", sentiment);
@@ -21229,7 +21282,7 @@ class ExotelAIAssistController extends EventEmitter {
21229
21282
  this.emit("streamState", state);
21230
21283
  if (state === "connected" || state === "throttled") {
21231
21284
  this._fireReady();
21232
- } else if (state === "disconnected" && this.readyFired) {
21285
+ } else if ((state === "connection_timeout" || state === "disconnected") && this.readyFired) {
21233
21286
  this.readyFired = false;
21234
21287
  this.emit("onReady", false);
21235
21288
  }
@@ -21293,9 +21346,20 @@ function useExotelAIAssist(params) {
21293
21346
  var _a;
21294
21347
  return (_a = controllerRef.current) === null || _a === void 0 ? void 0 : _a.setParams(patch);
21295
21348
  }, []);
21349
+ const sendSuggestionFeedback = reactExports.useCallback((sequence, feedbackType, badFeedbackReason = null) => {
21350
+ var _a, _b;
21351
+ const sent = (_b = (_a = controllerRef.current) === null || _a === void 0 ? void 0 : _a.sendSuggestionFeedback(sequence, feedbackType, badFeedbackReason)) !== null && _b !== void 0 ? _b : false;
21352
+ if (sent) {
21353
+ setSuggestions(prev => prev.map(s => s.sequence === sequence ? Object.assign(Object.assign({}, s), {
21354
+ feedbackType,
21355
+ badFeedbackReason
21356
+ }) : s));
21357
+ }
21358
+ return sent;
21359
+ }, []);
21296
21360
  return {
21297
- isReady,
21298
21361
  streamState,
21362
+ isReady,
21299
21363
  suggestions,
21300
21364
  transcripts,
21301
21365
  sentiment,
@@ -21303,7 +21367,8 @@ function useExotelAIAssist(params) {
21303
21367
  lastError,
21304
21368
  connect,
21305
21369
  disconnect,
21306
- setParams
21370
+ setParams,
21371
+ sendSuggestionFeedback
21307
21372
  };
21308
21373
  }
21309
21374
 
@@ -21332,7 +21397,7 @@ function styleInject(css, ref) {
21332
21397
  }
21333
21398
  }
21334
21399
 
21335
- var css_248z = ".oa-panel{background:#fff;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif}.oa-panel,.oa-theme-root{display:flex;flex-direction:column;height:100%;min-height:0;width:100%}.oa-theme-root{position:relative}.oa-header-title{color:#111827;font-size:16px;font-weight:600;line-height:20px;margin:0}.oa-tabs{width:100%}.oa-tabs-content{flex:1;min-height:0;overflow:hidden}.oa-tabs-content[data-state=inactive]{display:none!important}.oa-tabs-content[data-state=active]{display:flex;flex-direction:column}.oa-tabs-list{border-bottom:1px solid #e5e7eb;display:flex;gap:20px;padding:0}.oa-tabs-trigger{align-items:center;background:transparent;border:none;border-radius:0;color:#6b7280;cursor:pointer;display:inline-flex;font-size:15px;font-weight:500;gap:6px;outline:none;padding:10px 0;position:relative}.oa-tabs-trigger:hover{color:#374151}.oa-tabs-trigger[data-state=active]{color:#2563eb}.oa-tabs-trigger[data-state=active]:after{background:#2563eb;border-radius:2px;bottom:-1px;content:\"\";height:2px;left:0;position:absolute;right:0}.oa-suggestion-card{border:1px solid rgba(17,24,39,.04);border-radius:12px;padding:12px;text-align:left}.oa-suggestion-card--recent-wrapper{animation:oa-suggestion-enter .4s ease-out,oa-border-shimmer 3s ease-in-out .4s infinite;background:linear-gradient(90deg,#7c3aed,#e11d48,#7c3aed);background-size:200% 100%;border-radius:12px;padding:2px;text-align:left}.oa-suggestion-card--recent-wrapper .oa-suggestion-card--recent{border:none}.oa-suggestion-card--recent{animation:none;background:linear-gradient(90deg,#f5f3ff 0,#fef3f2 50%,#fff7ed);border-radius:9px}.oa-suggestion-card--older{background:#f5f5f5;border:1px solid #e5e7eb}.oa-suggestion-card--older .oa-suggestion-text{color:#252525}@keyframes oa-suggestion-enter{0%{opacity:.3;transform:scale(.97)}to{opacity:1;transform:scale(1)}}@keyframes oa-border-shimmer{0%,to{background-position:0 50%}50%{background-position:100% 50%}}.oa-suggestion-text{color:#111827;font-size:13px;line-height:18px;white-space:pre-wrap}.oa-copy-icon{align-items:center;background:#fff;border:1px solid #e5e7eb;border-radius:8px;color:#6b7280;cursor:pointer;display:inline-flex;height:32px;justify-content:center;min-height:32px;min-width:32px;padding:0;width:32px}.oa-copy-icon:hover{background:#f9fafb;color:#374151}@keyframes oa-toast-in{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}.oa-stream-overlay{display:flex;flex:1;flex-direction:column;min-height:0}.oa-stream-banner{align-items:flex-start;background:#fef3c7;border:1px solid #fde68a;border-radius:8px;color:#92400e;display:flex;font-size:13px;gap:8px;line-height:1.4;padding:10px 12px}.oa-stream-banner-icon{align-items:center;display:inline-flex;flex-shrink:0;margin-top:1px}.oa-stream-banner-text{flex:1}.oa-stream-empty{flex:1;gap:16px;justify-content:center;min-height:0}.oa-stream-empty,.oa-stream-empty-text{align-items:center;display:flex;flex-direction:column}.oa-stream-empty-text{gap:6px;text-align:center}.oa-stream-empty-title{color:#111827;font-size:16px;font-weight:700;line-height:20px}.oa-stream-empty-subtitle{color:#6b7280;font-size:14px;line-height:20px}.oa-throttle-banner{align-items:flex-start;background:#fcefee;border:1px solid #e8cecb;border-radius:8px;color:#833d09;display:flex;font-size:13px;gap:8px;line-height:1.4;padding:10px 12px}.oa-throttle-banner-icon{align-items:center;display:inline-flex;flex-shrink:0;margin-top:1px}.oa-throttle-banner-text{flex:1}.oa-starting-loader{align-items:center;display:flex;flex:1;flex-direction:column;gap:16px;height:100%;justify-content:center;min-height:0}.oa-starting-spinner{animation:oa-spinner-rotate 1.4s linear infinite;height:48px;width:48px}.oa-starting-spinner-track{stroke:#e5e7eb}.oa-starting-spinner-arc{stroke:#2563eb;stroke-dasharray:80,126;stroke-dashoffset:0;animation:oa-spinner-dash 1.4s ease-in-out infinite}@keyframes oa-spinner-rotate{to{transform:rotate(1turn)}}@keyframes oa-spinner-dash{0%{stroke-dasharray:1,126;stroke-dashoffset:0}50%{stroke-dasharray:80,126;stroke-dashoffset:-35}to{stroke-dasharray:80,126;stroke-dashoffset:-124}}.oa-starting-loader-text{color:#111827;font-size:16px;font-weight:600;line-height:20px}.oa-muted{color:#6b7280}";
21400
+ var css_248z = ".oa-panel{background:#fff;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif}.oa-panel,.oa-theme-root{display:flex;flex-direction:column;height:100%;min-height:0;width:100%}.oa-theme-root{position:relative}.oa-header-title{color:#111827;font-size:16px;font-weight:600;line-height:20px;margin:0}.oa-tabs{width:100%}.oa-tabs-content{flex:1;min-height:0;overflow:hidden}.oa-tabs-content[data-state=inactive]{display:none!important}.oa-tabs-content[data-state=active]{display:flex;flex-direction:column}.oa-tabs-list{border-bottom:1px solid #e5e7eb;display:flex;gap:20px;padding:0}.oa-tabs-trigger{align-items:center;background:transparent;border:none;border-radius:0;color:#6b7280;cursor:pointer;display:inline-flex;font-size:15px;font-weight:500;gap:6px;outline:none;padding:10px 0;position:relative}.oa-tabs-trigger:hover{color:#374151}.oa-tabs-trigger[data-state=active]{color:#2563eb}.oa-tabs-trigger[data-state=active]:after{background:#2563eb;border-radius:2px;bottom:-1px;content:\"\";height:2px;left:0;position:absolute;right:0}.oa-suggestion-card{border:1px solid rgba(17,24,39,.04);border-radius:12px;padding:12px;text-align:left}.oa-suggestion-card--recent-wrapper{animation:oa-suggestion-enter .4s ease-out,oa-border-shimmer 3s ease-in-out .4s infinite;background:linear-gradient(90deg,#7c3aed,#e11d48,#7c3aed);background-size:200% 100%;border-radius:12px;padding:2px;text-align:left}.oa-suggestion-card--recent-wrapper .oa-suggestion-card--recent{border:none}.oa-suggestion-card--recent{animation:none;background:linear-gradient(90deg,#f5f3ff 0,#fef3f2 50%,#fff7ed);border-radius:9px}.oa-suggestion-card--older{background:#f5f5f5;border:1px solid #e5e7eb}.oa-suggestion-card--older .oa-suggestion-text{color:#252525}@keyframes oa-suggestion-enter{0%{opacity:.3;transform:scale(.97)}to{opacity:1;transform:scale(1)}}@keyframes oa-border-shimmer{0%,to{background-position:0 50%}50%{background-position:100% 50%}}.oa-suggestion-text{color:#111827;font-size:13px;line-height:18px;white-space:pre-wrap}.oa-copy-icon,.oa-feedback-icon{align-items:center;background:#fff;border:1px solid #e5e7eb;border-radius:8px;color:#6b7280;cursor:pointer;display:inline-flex;height:32px;justify-content:center;min-height:32px;min-width:32px;padding:0;width:32px}.oa-copy-icon:hover,.oa-feedback-icon:hover{background:#f9fafb;color:#374151}.oa-feedback-icon--positive{background:#dcfce7!important;border-color:#86efac!important;color:#16a34a!important}.oa-feedback-icon--negative{background:#fee2e2!important;border-color:#fca5a5!important;color:#dc2626!important}.oa-feedback-icon:disabled{cursor:default;opacity:.5}.oa-feedback-icon--negative:disabled,.oa-feedback-icon--positive:disabled{opacity:1}.oa-feedback-reason-btn{align-items:center;background:#fef2f2;border:1px solid #fecaca;border-radius:16px;color:#991b1b;cursor:pointer;display:inline-flex;font-family:inherit;font-size:12px;font-weight:500;line-height:18px;padding:4px 12px;transition:background .15s,border-color .15s}.oa-feedback-reason-btn:hover:not(:disabled){background:#fee2e2;border-color:#f87171}.oa-feedback-reason-btn--selected{background:#dc2626!important;border-color:#dc2626!important;color:#fff!important}.oa-feedback-reason-btn:disabled{cursor:default;opacity:.45}@keyframes oa-toast-in{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}.oa-stream-overlay{display:flex;flex:1;flex-direction:column;min-height:0}.oa-stream-banner{align-items:flex-start;background:#fef3c7;border:1px solid #fde68a;border-radius:8px;color:#92400e;display:flex;font-size:13px;gap:8px;line-height:1.4;padding:10px 12px}.oa-stream-banner-icon{align-items:center;display:inline-flex;flex-shrink:0;margin-top:1px}.oa-stream-banner-text{flex:1}.oa-stream-empty{flex:1;gap:16px;justify-content:center;min-height:0}.oa-stream-empty,.oa-stream-empty-text{align-items:center;display:flex;flex-direction:column}.oa-stream-empty-text{gap:6px;text-align:center}.oa-stream-empty-title{color:#111827;font-size:16px;font-weight:700;line-height:20px}.oa-stream-empty-subtitle{color:#6b7280;font-size:14px;line-height:20px}.oa-throttle-banner{align-items:flex-start;background:#fcefee;border:1px solid #e8cecb;border-radius:8px;color:#833d09;display:flex;font-size:13px;gap:8px;line-height:1.4;padding:10px 12px}.oa-throttle-banner-icon{align-items:center;display:inline-flex;flex-shrink:0;margin-top:1px}.oa-throttle-banner-text{flex:1}.oa-starting-loader{align-items:center;display:flex;flex:1;flex-direction:column;gap:16px;height:100%;justify-content:center;min-height:0}.oa-starting-spinner{animation:oa-spinner-rotate 1.4s linear infinite;height:48px;width:48px}.oa-starting-spinner-track{stroke:#e5e7eb}.oa-starting-spinner-arc{stroke:#2563eb;stroke-dasharray:80,126;stroke-dashoffset:0;animation:oa-spinner-dash 1.4s ease-in-out infinite}@keyframes oa-spinner-rotate{to{transform:rotate(1turn)}}@keyframes oa-spinner-dash{0%{stroke-dasharray:1,126;stroke-dashoffset:0}50%{stroke-dasharray:80,126;stroke-dashoffset:-35}to{stroke-dasharray:80,126;stroke-dashoffset:-124}}.oa-starting-loader-text{color:#111827;font-size:16px;font-weight:600;line-height:20px}.oa-muted{color:#6b7280}";
21336
21401
  styleInject(css_248z);
21337
21402
 
21338
21403
  function Sparkle() {
@@ -21680,9 +21745,17 @@ const EmptyState = ({
21680
21745
  function SuggestionsTab({
21681
21746
  suggestions,
21682
21747
  connected,
21683
- botConfig
21748
+ botConfig,
21749
+ sendSuggestionFeedback
21684
21750
  }) {
21751
+ var _a, _b, _c;
21685
21752
  const toast = useToast();
21753
+ const [expandedSequence, setExpandedSequence] = reactExports.useState(null);
21754
+ const feedbackEnabled = ((_a = botConfig === null || botConfig === void 0 ? void 0 : botConfig.suggestion) === null || _a === void 0 ? void 0 : _a.feedback_enabled) === true;
21755
+ const badFeedbackOptions = reactExports.useMemo(() => {
21756
+ var _a, _b;
21757
+ return (_b = (_a = botConfig === null || botConfig === void 0 ? void 0 : botConfig.suggestion) === null || _a === void 0 ? void 0 : _a.bad_feedback_options) !== null && _b !== void 0 ? _b : [];
21758
+ }, [(_b = botConfig === null || botConfig === void 0 ? void 0 : botConfig.suggestion) === null || _b === void 0 ? void 0 : _b.bad_feedback_options]);
21686
21759
  const handleCopy = async text => {
21687
21760
  try {
21688
21761
  await navigator.clipboard.writeText(text);
@@ -21691,13 +21764,36 @@ function SuggestionsTab({
21691
21764
  toast.error("Failed to copy");
21692
21765
  }
21693
21766
  };
21767
+ const handleThumbsUp = reactExports.useCallback((sequence, currentFeedback) => {
21768
+ if (!feedbackEnabled) return;
21769
+ const newType = currentFeedback === "good" ? null : "good";
21770
+ sendSuggestionFeedback(sequence, newType, null);
21771
+ setExpandedSequence(null);
21772
+ }, [sendSuggestionFeedback, feedbackEnabled]);
21773
+ const handleThumbsDown = reactExports.useCallback((sequence, currentFeedback) => {
21774
+ if (!feedbackEnabled) return;
21775
+ if (currentFeedback === "bad") {
21776
+ sendSuggestionFeedback(sequence, null, null);
21777
+ setExpandedSequence(null);
21778
+ return;
21779
+ }
21780
+ sendSuggestionFeedback(sequence, "bad", null);
21781
+ if (badFeedbackOptions.length > 0) {
21782
+ setExpandedSequence(sequence);
21783
+ }
21784
+ }, [sendSuggestionFeedback, badFeedbackOptions, feedbackEnabled]);
21785
+ const handleBadFeedbackReason = reactExports.useCallback((sequence, reason) => {
21786
+ if (!feedbackEnabled) return;
21787
+ sendSuggestionFeedback(sequence, "bad", reason);
21788
+ setExpandedSequence(null);
21789
+ }, [sendSuggestionFeedback, feedbackEnabled]);
21694
21790
  if (!connected) {
21695
21791
  return jsxRuntimeExports.jsx(EmptyState, {
21696
21792
  title: "Assistant is inactive",
21697
21793
  subtitle: "Start a call to receive real-time suggestions."
21698
21794
  });
21699
21795
  }
21700
- if ((botConfig === null || botConfig === void 0 ? void 0 : botConfig.suggestions) === false) {
21796
+ if (((_c = botConfig === null || botConfig === void 0 ? void 0 : botConfig.suggestion) === null || _c === void 0 ? void 0 : _c.enabled) === false) {
21701
21797
  return jsxRuntimeExports.jsx(EmptyState, {
21702
21798
  title: "Suggestions are disabled",
21703
21799
  subtitle: "Suggestions are disabled in the bot configuration. Please contact the administrator to enable it."
@@ -21731,21 +21827,37 @@ function SuggestionsTab({
21731
21827
  children: displayed.map((suggestion, index) => {
21732
21828
  const isRecent = index === 0;
21733
21829
  const cardClass = isRecent ? "oa-suggestion-card oa-suggestion-card--recent" : "oa-suggestion-card oa-suggestion-card--older";
21830
+ const showReasons = suggestion.feedbackType === "bad" || expandedSequence === suggestion.sequence;
21734
21831
  return jsxRuntimeExports.jsxs("div", {
21735
- style: {
21736
- display: "flex",
21737
- flexDirection: "row",
21738
- alignItems: "flex-start",
21739
- gap: "8px"
21740
- },
21741
- children: [isRecent ? jsxRuntimeExports.jsx("div", {
21742
- className: "oa-suggestion-card--recent-wrapper",
21832
+ children: [jsxRuntimeExports.jsxs("div", {
21743
21833
  style: {
21744
- flex: "1 1 auto",
21745
- maxWidth: "80%"
21834
+ display: "flex",
21835
+ flexDirection: "row",
21836
+ alignItems: "flex-start",
21837
+ gap: "8px"
21746
21838
  },
21747
- children: jsxRuntimeExports.jsx("div", {
21839
+ children: [isRecent ? jsxRuntimeExports.jsx("div", {
21840
+ className: "oa-suggestion-card--recent-wrapper",
21841
+ style: {
21842
+ flex: "1 1 auto",
21843
+ maxWidth: "72%"
21844
+ },
21845
+ children: jsxRuntimeExports.jsx("div", {
21846
+ className: cardClass,
21847
+ children: jsxRuntimeExports.jsx("span", {
21848
+ className: "oa-suggestion-text",
21849
+ style: {
21850
+ fontSize: "15px"
21851
+ },
21852
+ children: suggestion.text
21853
+ })
21854
+ })
21855
+ }) : jsxRuntimeExports.jsx("div", {
21748
21856
  className: cardClass,
21857
+ style: {
21858
+ flex: "1 1 auto",
21859
+ maxWidth: "72%"
21860
+ },
21749
21861
  children: jsxRuntimeExports.jsx("span", {
21750
21862
  className: "oa-suggestion-text",
21751
21863
  style: {
@@ -21753,31 +21865,57 @@ function SuggestionsTab({
21753
21865
  },
21754
21866
  children: suggestion.value
21755
21867
  })
21756
- })
21757
- }) : jsxRuntimeExports.jsx("div", {
21758
- className: cardClass,
21759
- style: {
21760
- flex: "1 1 auto",
21761
- maxWidth: "80%"
21762
- },
21763
- children: jsxRuntimeExports.jsx("span", {
21764
- className: "oa-suggestion-text",
21868
+ }), jsxRuntimeExports.jsxs("div", {
21765
21869
  style: {
21766
- fontSize: "15px"
21870
+ display: "flex",
21871
+ flexDirection: "column",
21872
+ gap: "4px",
21873
+ flexShrink: 0,
21874
+ marginTop: "10px"
21767
21875
  },
21768
- children: suggestion.value
21769
- })
21770
- }), jsxRuntimeExports.jsx("button", {
21771
- className: "oa-copy-icon",
21772
- "aria-label": "Copy suggestion",
21773
- onClick: () => handleCopy(suggestion.value),
21876
+ children: [jsxRuntimeExports.jsx("button", {
21877
+ className: "oa-copy-icon",
21878
+ "aria-label": "Copy suggestion",
21879
+ onClick: () => handleCopy(suggestion.text),
21880
+ children: jsxRuntimeExports.jsx(Copy, {
21881
+ size: 14
21882
+ })
21883
+ }), feedbackEnabled && jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, {
21884
+ children: [jsxRuntimeExports.jsx("button", {
21885
+ className: `oa-feedback-icon ${suggestion.feedbackType === "good" ? "oa-feedback-icon--positive" : ""}`,
21886
+ "aria-label": "Mark as helpful",
21887
+ onClick: () => handleThumbsUp(suggestion.sequence, suggestion.feedbackType),
21888
+ style: {
21889
+ color: suggestion.feedbackType === "good" ? "#16a34a" : undefined
21890
+ },
21891
+ children: jsxRuntimeExports.jsx(ThumbsUp, {
21892
+ size: 14
21893
+ })
21894
+ }), jsxRuntimeExports.jsx("button", {
21895
+ className: `oa-feedback-icon ${suggestion.feedbackType === "bad" ? "oa-feedback-icon--negative" : ""}`,
21896
+ "aria-label": "Mark as not helpful",
21897
+ onClick: () => handleThumbsDown(suggestion.sequence, suggestion.feedbackType),
21898
+ style: {
21899
+ color: suggestion.feedbackType === "bad" ? "#dc2626" : undefined
21900
+ },
21901
+ children: jsxRuntimeExports.jsx(ThumbsDown, {
21902
+ size: 14
21903
+ })
21904
+ })]
21905
+ })]
21906
+ })]
21907
+ }), showReasons && badFeedbackOptions.length > 0 && jsxRuntimeExports.jsx("div", {
21774
21908
  style: {
21775
- flexShrink: 0,
21776
- marginTop: "10px"
21909
+ display: "flex",
21910
+ gap: "8px",
21911
+ flexWrap: "wrap",
21912
+ marginTop: "8px"
21777
21913
  },
21778
- children: jsxRuntimeExports.jsx(Copy, {
21779
- size: 14
21780
- })
21914
+ children: badFeedbackOptions.map(reason => jsxRuntimeExports.jsx("button", {
21915
+ className: `oa-feedback-reason-btn ${suggestion.badFeedbackReason === reason ? "oa-feedback-reason-btn--selected" : ""}`,
21916
+ onClick: () => handleBadFeedbackReason(suggestion.sequence, reason),
21917
+ children: reason
21918
+ }, reason))
21781
21919
  })]
21782
21920
  }, suggestion.id);
21783
21921
  })
@@ -21876,12 +22014,13 @@ function ExotelAIAssist(_a) {
21876
22014
  } = _a,
21877
22015
  params = __rest(_a, ["className", "onReady"]);
21878
22016
  const {
21879
- isReady,
21880
22017
  streamState,
22018
+ isReady,
21881
22019
  suggestions,
21882
22020
  transcripts,
21883
22021
  sentiment,
21884
- botConfig
22022
+ botConfig,
22023
+ sendSuggestionFeedback
21885
22024
  } = useExotelAIAssist(params);
21886
22025
  const onReadyRef = reactExports.useRef(onReady);
21887
22026
  onReadyRef.current = onReady;
@@ -21899,7 +22038,7 @@ function ExotelAIAssist(_a) {
21899
22038
  warningBannerMessage: "AI Assist couldn't join this call \u2014 capacity is full. It should be ready for your next call."
21900
22039
  });
21901
22040
  }
21902
- if (streamState === "disconnected") {
22041
+ if (streamState === "connection_timeout") {
21903
22042
  return jsxRuntimeExports.jsx(EmptyState, {
21904
22043
  title: "AI Assist is currently inactive",
21905
22044
  subtitle: "we were unable to establish a connection."
@@ -21959,7 +22098,8 @@ function ExotelAIAssist(_a) {
21959
22098
  children: jsxRuntimeExports.jsx(SuggestionsTab, {
21960
22099
  suggestions: suggestions,
21961
22100
  connected: connected,
21962
- botConfig: botConfig
22101
+ botConfig: botConfig,
22102
+ sendSuggestionFeedback: sendSuggestionFeedback
21963
22103
  })
21964
22104
  }), jsxRuntimeExports.jsx(Content$1, {
21965
22105
  value: "transcript",