@exotel-npm-dev/exotel-ai-assist 1.2.6 → 1.2.7

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));
@@ -1737,11 +1756,11 @@ class ExotelAIAssistController extends EventEmitter {
1737
1756
  this.emit("onReady", true);
1738
1757
  }
1739
1758
  _handleServerPayload(raw) {
1740
- var _a, _b, _c;
1759
+ var _a, _b;
1741
1760
  let parsed;
1742
1761
  try {
1743
1762
  parsed = JSON.parse(raw);
1744
- } catch (_d) {
1763
+ } catch (_c) {
1745
1764
  this.emit("error", new Error(`Failed to parse server message: ${raw}`));
1746
1765
  return;
1747
1766
  }
@@ -1774,7 +1793,7 @@ class ExotelAIAssistController extends EventEmitter {
1774
1793
  for (const event of events) {
1775
1794
  if (!event) continue;
1776
1795
  if (event.event_type === "transcript" && event.value) {
1777
- const transcripts = Array.isArray(event.value) ? event.value : (_c = event.transcript) !== null && _c !== void 0 ? _c : [];
1796
+ const transcripts = event.value;
1778
1797
  const lines = transcripts.map(msg => {
1779
1798
  var _a, _b, _c;
1780
1799
  const first = (_a = msg.transcript_segments) === null || _a === void 0 ? void 0 : _a[0];
@@ -1791,9 +1810,12 @@ class ExotelAIAssistController extends EventEmitter {
1791
1810
  }
1792
1811
  if (event.event_type === "suggestion" && event.value) {
1793
1812
  const suggestion = {
1794
- id: Utils.getUniqueId(),
1813
+ id: String(event.value.sequence),
1795
1814
  value: event.value.text,
1796
- timestamp: now
1815
+ timestamp: now,
1816
+ sequence: event.value.sequence,
1817
+ feedbackType: null,
1818
+ badFeedbackReason: null
1797
1819
  };
1798
1820
  this.emit("suggestion", suggestion);
1799
1821
  }
@@ -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));
@@ -21155,11 +21198,11 @@ class ExotelAIAssistController extends EventEmitter {
21155
21198
  this.emit("onReady", true);
21156
21199
  }
21157
21200
  _handleServerPayload(raw) {
21158
- var _a, _b, _c;
21201
+ var _a, _b;
21159
21202
  let parsed;
21160
21203
  try {
21161
21204
  parsed = JSON.parse(raw);
21162
- } catch (_d) {
21205
+ } catch (_c) {
21163
21206
  this.emit("error", new Error(`Failed to parse server message: ${raw}`));
21164
21207
  return;
21165
21208
  }
@@ -21192,7 +21235,7 @@ class ExotelAIAssistController extends EventEmitter {
21192
21235
  for (const event of events) {
21193
21236
  if (!event) continue;
21194
21237
  if (event.event_type === "transcript" && event.value) {
21195
- const transcripts = Array.isArray(event.value) ? event.value : (_c = event.transcript) !== null && _c !== void 0 ? _c : [];
21238
+ const transcripts = event.value;
21196
21239
  const lines = transcripts.map(msg => {
21197
21240
  var _a, _b, _c;
21198
21241
  const first = (_a = msg.transcript_segments) === null || _a === void 0 ? void 0 : _a[0];
@@ -21209,9 +21252,12 @@ class ExotelAIAssistController extends EventEmitter {
21209
21252
  }
21210
21253
  if (event.event_type === "suggestion" && event.value) {
21211
21254
  const suggestion = {
21212
- id: Utils.getUniqueId(),
21255
+ id: String(event.value.sequence),
21213
21256
  value: event.value.text,
21214
- timestamp: now
21257
+ timestamp: now,
21258
+ sequence: event.value.sequence,
21259
+ feedbackType: null,
21260
+ badFeedbackReason: null
21215
21261
  };
21216
21262
  this.emit("suggestion", suggestion);
21217
21263
  }
@@ -21293,9 +21339,20 @@ function useExotelAIAssist(params) {
21293
21339
  var _a;
21294
21340
  return (_a = controllerRef.current) === null || _a === void 0 ? void 0 : _a.setParams(patch);
21295
21341
  }, []);
21342
+ const sendSuggestionFeedback = reactExports.useCallback((sequence, feedbackType, badFeedbackReason = null) => {
21343
+ var _a, _b;
21344
+ const sent = (_b = (_a = controllerRef.current) === null || _a === void 0 ? void 0 : _a.sendSuggestionFeedback(sequence, feedbackType, badFeedbackReason)) !== null && _b !== void 0 ? _b : false;
21345
+ if (sent) {
21346
+ setSuggestions(prev => prev.map(s => s.sequence === sequence ? Object.assign(Object.assign({}, s), {
21347
+ feedbackType,
21348
+ badFeedbackReason
21349
+ }) : s));
21350
+ }
21351
+ return sent;
21352
+ }, []);
21296
21353
  return {
21297
- isReady,
21298
21354
  streamState,
21355
+ isReady,
21299
21356
  suggestions,
21300
21357
  transcripts,
21301
21358
  sentiment,
@@ -21303,7 +21360,8 @@ function useExotelAIAssist(params) {
21303
21360
  lastError,
21304
21361
  connect,
21305
21362
  disconnect,
21306
- setParams
21363
+ setParams,
21364
+ sendSuggestionFeedback
21307
21365
  };
21308
21366
  }
21309
21367
 
@@ -21332,7 +21390,7 @@ function styleInject(css, ref) {
21332
21390
  }
21333
21391
  }
21334
21392
 
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}";
21393
+ 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
21394
  styleInject(css_248z);
21337
21395
 
21338
21396
  function Sparkle() {
@@ -21680,9 +21738,17 @@ const EmptyState = ({
21680
21738
  function SuggestionsTab({
21681
21739
  suggestions,
21682
21740
  connected,
21683
- botConfig
21741
+ botConfig,
21742
+ sendSuggestionFeedback
21684
21743
  }) {
21744
+ var _a, _b, _c;
21685
21745
  const toast = useToast();
21746
+ const [expandedSequence, setExpandedSequence] = reactExports.useState(null);
21747
+ const feedbackEnabled = ((_a = botConfig === null || botConfig === void 0 ? void 0 : botConfig.suggestion) === null || _a === void 0 ? void 0 : _a.feedback_enabled) === true;
21748
+ const badFeedbackOptions = reactExports.useMemo(() => {
21749
+ var _a, _b;
21750
+ 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 : [];
21751
+ }, [(_b = botConfig === null || botConfig === void 0 ? void 0 : botConfig.suggestion) === null || _b === void 0 ? void 0 : _b.bad_feedback_options]);
21686
21752
  const handleCopy = async text => {
21687
21753
  try {
21688
21754
  await navigator.clipboard.writeText(text);
@@ -21691,13 +21757,33 @@ function SuggestionsTab({
21691
21757
  toast.error("Failed to copy");
21692
21758
  }
21693
21759
  };
21760
+ const handleThumbsUp = reactExports.useCallback((sequence, currentFeedback) => {
21761
+ const newType = currentFeedback === "good" ? null : "good";
21762
+ sendSuggestionFeedback(sequence, newType, null);
21763
+ setExpandedSequence(null);
21764
+ }, [sendSuggestionFeedback]);
21765
+ const handleThumbsDown = reactExports.useCallback((sequence, currentFeedback) => {
21766
+ if (currentFeedback === "bad") {
21767
+ sendSuggestionFeedback(sequence, null, null);
21768
+ setExpandedSequence(null);
21769
+ return;
21770
+ }
21771
+ sendSuggestionFeedback(sequence, "bad", null);
21772
+ if (badFeedbackOptions.length > 0) {
21773
+ setExpandedSequence(sequence);
21774
+ }
21775
+ }, [sendSuggestionFeedback, badFeedbackOptions]);
21776
+ const handleBadFeedbackReason = reactExports.useCallback((sequence, reason) => {
21777
+ sendSuggestionFeedback(sequence, "bad", reason);
21778
+ setExpandedSequence(null);
21779
+ }, [sendSuggestionFeedback]);
21694
21780
  if (!connected) {
21695
21781
  return jsxRuntimeExports.jsx(EmptyState, {
21696
21782
  title: "Assistant is inactive",
21697
21783
  subtitle: "Start a call to receive real-time suggestions."
21698
21784
  });
21699
21785
  }
21700
- if ((botConfig === null || botConfig === void 0 ? void 0 : botConfig.suggestions) === false) {
21786
+ if (((_c = botConfig === null || botConfig === void 0 ? void 0 : botConfig.suggestion) === null || _c === void 0 ? void 0 : _c.enabled) === false) {
21701
21787
  return jsxRuntimeExports.jsx(EmptyState, {
21702
21788
  title: "Suggestions are disabled",
21703
21789
  subtitle: "Suggestions are disabled in the bot configuration. Please contact the administrator to enable it."
@@ -21731,21 +21817,44 @@ function SuggestionsTab({
21731
21817
  children: displayed.map((suggestion, index) => {
21732
21818
  const isRecent = index === 0;
21733
21819
  const cardClass = isRecent ? "oa-suggestion-card oa-suggestion-card--recent" : "oa-suggestion-card oa-suggestion-card--older";
21820
+ const showReasons = suggestion.feedbackType === "bad" || expandedSequence === suggestion.sequence;
21821
+ const cardMinHeight = feedbackEnabled ? "108px" : undefined;
21734
21822
  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",
21823
+ children: [jsxRuntimeExports.jsxs("div", {
21743
21824
  style: {
21744
- flex: "1 1 auto",
21745
- maxWidth: "80%"
21825
+ display: "flex",
21826
+ flexDirection: "row",
21827
+ alignItems: "flex-start",
21828
+ gap: "8px"
21746
21829
  },
21747
- children: jsxRuntimeExports.jsx("div", {
21830
+ children: [isRecent ? jsxRuntimeExports.jsx("div", {
21831
+ className: "oa-suggestion-card--recent-wrapper",
21832
+ style: {
21833
+ flex: "1 1 auto",
21834
+ maxWidth: "72%",
21835
+ minHeight: cardMinHeight
21836
+ },
21837
+ children: jsxRuntimeExports.jsx("div", {
21838
+ className: cardClass,
21839
+ style: {
21840
+ minHeight: cardMinHeight ? `calc(${cardMinHeight} - 4px)` : undefined,
21841
+ height: "100%"
21842
+ },
21843
+ children: jsxRuntimeExports.jsx("span", {
21844
+ className: "oa-suggestion-text",
21845
+ style: {
21846
+ fontSize: "15px"
21847
+ },
21848
+ children: suggestion.value
21849
+ })
21850
+ })
21851
+ }) : jsxRuntimeExports.jsx("div", {
21748
21852
  className: cardClass,
21853
+ style: {
21854
+ flex: "1 1 auto",
21855
+ maxWidth: "72%",
21856
+ minHeight: cardMinHeight
21857
+ },
21749
21858
  children: jsxRuntimeExports.jsx("span", {
21750
21859
  className: "oa-suggestion-text",
21751
21860
  style: {
@@ -21753,31 +21862,57 @@ function SuggestionsTab({
21753
21862
  },
21754
21863
  children: suggestion.value
21755
21864
  })
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",
21865
+ }), jsxRuntimeExports.jsxs("div", {
21765
21866
  style: {
21766
- fontSize: "15px"
21867
+ display: "flex",
21868
+ flexDirection: "column",
21869
+ gap: "4px",
21870
+ flexShrink: 0,
21871
+ marginTop: "10px"
21767
21872
  },
21768
- children: suggestion.value
21769
- })
21770
- }), jsxRuntimeExports.jsx("button", {
21771
- className: "oa-copy-icon",
21772
- "aria-label": "Copy suggestion",
21773
- onClick: () => handleCopy(suggestion.value),
21873
+ children: [jsxRuntimeExports.jsx("button", {
21874
+ className: "oa-copy-icon",
21875
+ "aria-label": "Copy suggestion",
21876
+ onClick: () => handleCopy(suggestion.value),
21877
+ children: jsxRuntimeExports.jsx(Copy, {
21878
+ size: 14
21879
+ })
21880
+ }), feedbackEnabled && jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, {
21881
+ children: [jsxRuntimeExports.jsx("button", {
21882
+ className: `oa-feedback-icon ${suggestion.feedbackType === "good" ? "oa-feedback-icon--positive" : ""}`,
21883
+ "aria-label": "Mark as helpful",
21884
+ onClick: () => handleThumbsUp(suggestion.sequence, suggestion.feedbackType),
21885
+ style: {
21886
+ color: suggestion.feedbackType === "good" ? "#16a34a" : undefined
21887
+ },
21888
+ children: jsxRuntimeExports.jsx(ThumbsUp, {
21889
+ size: 14
21890
+ })
21891
+ }), jsxRuntimeExports.jsx("button", {
21892
+ className: `oa-feedback-icon ${suggestion.feedbackType === "bad" ? "oa-feedback-icon--negative" : ""}`,
21893
+ "aria-label": "Mark as not helpful",
21894
+ onClick: () => handleThumbsDown(suggestion.sequence, suggestion.feedbackType),
21895
+ style: {
21896
+ color: suggestion.feedbackType === "bad" ? "#dc2626" : undefined
21897
+ },
21898
+ children: jsxRuntimeExports.jsx(ThumbsDown, {
21899
+ size: 14
21900
+ })
21901
+ })]
21902
+ })]
21903
+ })]
21904
+ }), showReasons && badFeedbackOptions.length > 0 && jsxRuntimeExports.jsx("div", {
21774
21905
  style: {
21775
- flexShrink: 0,
21776
- marginTop: "10px"
21906
+ display: "flex",
21907
+ gap: "8px",
21908
+ flexWrap: "wrap",
21909
+ marginTop: "8px"
21777
21910
  },
21778
- children: jsxRuntimeExports.jsx(Copy, {
21779
- size: 14
21780
- })
21911
+ children: badFeedbackOptions.map(reason => jsxRuntimeExports.jsx("button", {
21912
+ className: `oa-feedback-reason-btn ${suggestion.badFeedbackReason === reason ? "oa-feedback-reason-btn--selected" : ""}`,
21913
+ onClick: () => handleBadFeedbackReason(suggestion.sequence, reason),
21914
+ children: reason
21915
+ }, reason))
21781
21916
  })]
21782
21917
  }, suggestion.id);
21783
21918
  })
@@ -21876,12 +22011,13 @@ function ExotelAIAssist(_a) {
21876
22011
  } = _a,
21877
22012
  params = __rest(_a, ["className", "onReady"]);
21878
22013
  const {
21879
- isReady,
21880
22014
  streamState,
22015
+ isReady,
21881
22016
  suggestions,
21882
22017
  transcripts,
21883
22018
  sentiment,
21884
- botConfig
22019
+ botConfig,
22020
+ sendSuggestionFeedback
21885
22021
  } = useExotelAIAssist(params);
21886
22022
  const onReadyRef = reactExports.useRef(onReady);
21887
22023
  onReadyRef.current = onReady;
@@ -21959,7 +22095,8 @@ function ExotelAIAssist(_a) {
21959
22095
  children: jsxRuntimeExports.jsx(SuggestionsTab, {
21960
22096
  suggestions: suggestions,
21961
22097
  connected: connected,
21962
- botConfig: botConfig
22098
+ botConfig: botConfig,
22099
+ sendSuggestionFeedback: sendSuggestionFeedback
21963
22100
  })
21964
22101
  }), jsxRuntimeExports.jsx(Content$1, {
21965
22102
  value: "transcript",