@eshal-bot/chat-widget 0.1.72 → 0.1.73
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/chat-widget.esm.js +3 -3
- package/dist/chat-widget.esm.js.map +1 -0
- package/dist/chat-widget.js +167 -296
- package/dist/chat-widget.js.map +1 -0
- package/dist/chat-widget.min.js +3 -3
- package/dist/chat-widget.umd.js +3 -3
- package/dist/chat-widget.umd.js.map +1 -0
- package/package.json +1 -1
package/dist/chat-widget.js
CHANGED
|
@@ -8010,21 +8010,16 @@
|
|
|
8010
8010
|
* Sends a streaming chat request using fetch
|
|
8011
8011
|
* @param {object} request - Chat request object
|
|
8012
8012
|
* @param {string} apiBaseUrl - Base URL for the API
|
|
8013
|
-
* @param {Function|undefined} getAuthToken - async fn returning a Bearer token (in-app JWT path), or undefined (Mode B proxy)
|
|
8014
8013
|
*/
|
|
8015
|
-
const sendStreamingRequest = async (request, apiBaseUrl,
|
|
8014
|
+
const sendStreamingRequest = async (request, apiBaseUrl, organizationId) => {
|
|
8016
8015
|
const headers = {
|
|
8017
8016
|
'Content-Type': 'application/json',
|
|
8018
8017
|
Accept: 'text/event-stream'
|
|
8019
8018
|
};
|
|
8020
8019
|
|
|
8021
|
-
//
|
|
8022
|
-
|
|
8023
|
-
|
|
8024
|
-
const token = await getAuthToken();
|
|
8025
|
-
if (token) {
|
|
8026
|
-
headers['Authorization'] = "Bearer ".concat(token);
|
|
8027
|
-
}
|
|
8020
|
+
// Add organization ID header if provided
|
|
8021
|
+
if (organizationId) {
|
|
8022
|
+
headers['x-eshal-org'] = organizationId;
|
|
8028
8023
|
}
|
|
8029
8024
|
const response = await fetch("".concat(apiBaseUrl.replace(/\/$/, ''), "/api/v1/support"), {
|
|
8030
8025
|
method: 'POST',
|
|
@@ -8348,33 +8343,20 @@
|
|
|
8348
8343
|
// Handle text_chunk events
|
|
8349
8344
|
if (parsed.type === 'text_chunk') {
|
|
8350
8345
|
var _parsed$choices;
|
|
8351
|
-
// ALWAYS create the skeleton on the first text_chunk, even when it
|
|
8352
|
-
// carries no content (a role-only "skeleton" chunk — delta.role
|
|
8353
|
-
// without delta.content — is the backend's "an assistant turn is
|
|
8354
|
-
// starting" signal used by the input/source-clarification short-
|
|
8355
|
-
// circuits in chat_utils.py). Without this, a clarification turn
|
|
8356
|
-
// that emits [text_chunk role-only] -> [complete content+
|
|
8357
|
-
// finalContent] never created the assistant bubble (this branch
|
|
8358
|
-
// skipped on empty content; the COMPLETE branch's updateFinalMessage
|
|
8359
|
-
// requires lastMessage.role === 'assistant' to render — see
|
|
8360
|
-
// updateFinalMessage line ~189) → the follow-up question was
|
|
8361
|
-
// silently dropped, even though the wire carried everything
|
|
8362
|
-
// correctly. Mirrors handleStreamEvent (line ~257) and packages/
|
|
8363
|
-
// frontend ChatService.ts (line ~213) which both create the
|
|
8364
|
-
// skeleton unconditionally on the first text_chunk.
|
|
8365
|
-
if (!hasReceivedFirstChunk.current) {
|
|
8366
|
-
hasReceivedFirstChunk.current = true;
|
|
8367
|
-
setMessages(prev => {
|
|
8368
|
-
const lastMessage = prev[prev.length - 1];
|
|
8369
|
-
if (!lastMessage || lastMessage.role !== 'assistant' || !lastMessage.isProcessing) {
|
|
8370
|
-
const skeletonMessage = createSkeletonMessage();
|
|
8371
|
-
return [...(prev || []), skeletonMessage];
|
|
8372
|
-
}
|
|
8373
|
-
return prev;
|
|
8374
|
-
});
|
|
8375
|
-
}
|
|
8376
8346
|
const content = ((_parsed$choices = parsed.choices) === null || _parsed$choices === void 0 || (_parsed$choices = _parsed$choices[0]) === null || _parsed$choices === void 0 || (_parsed$choices = _parsed$choices.delta) === null || _parsed$choices === void 0 ? void 0 : _parsed$choices.content) || parsed.content;
|
|
8377
8347
|
if (content) {
|
|
8348
|
+
// Create skeleton on first chunk if not already created
|
|
8349
|
+
if (!hasReceivedFirstChunk.current) {
|
|
8350
|
+
hasReceivedFirstChunk.current = true;
|
|
8351
|
+
setMessages(prev => {
|
|
8352
|
+
const lastMessage = prev[prev.length - 1];
|
|
8353
|
+
if (!lastMessage || lastMessage.role !== 'assistant' || !lastMessage.isProcessing) {
|
|
8354
|
+
const skeletonMessage = createSkeletonMessage();
|
|
8355
|
+
return [...(prev || []), skeletonMessage];
|
|
8356
|
+
}
|
|
8357
|
+
return prev;
|
|
8358
|
+
});
|
|
8359
|
+
}
|
|
8378
8360
|
accumulatedText.current += content;
|
|
8379
8361
|
updateMessage(accumulatedText.current);
|
|
8380
8362
|
}
|
|
@@ -8777,27 +8759,31 @@
|
|
|
8777
8759
|
};
|
|
8778
8760
|
|
|
8779
8761
|
/**
|
|
8780
|
-
* Constructs the voice WebSocket base URL
|
|
8762
|
+
* Constructs the voice WebSocket base URL — ALWAYS derived from the API
|
|
8763
|
+
* base URL the widget is embedded with, as `wss://{host}/voice/ws`.
|
|
8781
8764
|
*
|
|
8782
|
-
*
|
|
8783
|
-
*
|
|
8784
|
-
*
|
|
8785
|
-
*
|
|
8786
|
-
*
|
|
8765
|
+
* `/voice/ws/{org}/{session}` is terminated by the frontend custom server
|
|
8766
|
+
* (packages/frontend/server.js), which proxies it in-cluster to
|
|
8767
|
+
* knowledge-api on `/ws`. The widget always talks to the same origin it
|
|
8768
|
+
* loaded its config from, so the WS endpoint is fully determined by
|
|
8769
|
+
* `apiBaseUrl`. There is intentionally NO override parameter: voice can
|
|
8770
|
+
* never be pointed at the (no-longer-public) knowledge-api LoadBalancer by
|
|
8771
|
+
* a stale embed option or backend config value.
|
|
8787
8772
|
*
|
|
8788
8773
|
* @param {string} apiBaseUrl - Frontend base URL, e.g. https://demo.eshal.ai
|
|
8789
|
-
* @
|
|
8790
|
-
* @returns {string} Voice WS base URL
|
|
8774
|
+
* @returns {string} Voice WS base, e.g. wss://demo.eshal.ai/voice/ws
|
|
8791
8775
|
*/
|
|
8792
|
-
const getWebSocketUrl =
|
|
8793
|
-
if (wsBaseUrl) return wsBaseUrl.replace(/\/$/, '');
|
|
8776
|
+
const getWebSocketUrl = apiBaseUrl => {
|
|
8794
8777
|
if (!apiBaseUrl) {
|
|
8778
|
+
// No base URL yet (config still loading) — safe public default.
|
|
8795
8779
|
return 'wss://dev.eshal.ai/voice/ws';
|
|
8796
8780
|
}
|
|
8797
8781
|
try {
|
|
8798
8782
|
const url = new URL(apiBaseUrl);
|
|
8783
|
+
// http→ws, https→wss; point at the frontend voice-proxy path.
|
|
8799
8784
|
url.protocol = url.protocol.replace(/^http/, 'ws');
|
|
8800
8785
|
url.pathname = '/voice/ws';
|
|
8786
|
+
// Clean URL without a trailing slash.
|
|
8801
8787
|
return url.toString().replace(/\/$/, '');
|
|
8802
8788
|
} catch (error) {
|
|
8803
8789
|
console.error("Invalid API base URL provided for WebSocket:", error);
|
|
@@ -9141,10 +9127,8 @@
|
|
|
9141
9127
|
welcomeMessage,
|
|
9142
9128
|
quickQuestions = [],
|
|
9143
9129
|
apiBaseUrl,
|
|
9144
|
-
wsBaseUrl,
|
|
9145
9130
|
apiKey,
|
|
9146
9131
|
organizationId,
|
|
9147
|
-
getAuthToken,
|
|
9148
9132
|
autoOpen,
|
|
9149
9133
|
openDelay,
|
|
9150
9134
|
darkMode,
|
|
@@ -9312,9 +9296,11 @@
|
|
|
9312
9296
|
const visitorProfileRef = reactExports.useRef(null);
|
|
9313
9297
|
const websocketUrl = reactExports.useMemo(() => {
|
|
9314
9298
|
if (!organizationId) return null;
|
|
9315
|
-
|
|
9299
|
+
// Voice WS base is always derived from apiBaseUrl (the frontend origin
|
|
9300
|
+
// the widget is embedded with) → wss://{host}/voice/ws. No override.
|
|
9301
|
+
const resolvedWsBaseUrl = getWebSocketUrl(apiBaseUrl);
|
|
9316
9302
|
return "".concat(resolvedWsBaseUrl, "/").concat(organizationId, "/").concat(bidiSessionId);
|
|
9317
|
-
}, [apiBaseUrl,
|
|
9303
|
+
}, [apiBaseUrl, organizationId, bidiSessionId]);
|
|
9318
9304
|
const getNextMessageId = reactExports.useCallback(() => {
|
|
9319
9305
|
const id = "msg-".concat(messageIdRef.current);
|
|
9320
9306
|
messageIdRef.current += 1;
|
|
@@ -9779,7 +9765,7 @@
|
|
|
9779
9765
|
if (apiType === 'userName' || apiType === 'email' || apiType === 'phone') {
|
|
9780
9766
|
// For onboarding fields, just consume the stream without processing.
|
|
9781
9767
|
// Single attempt — no retry, no user-visible partial state to clean up.
|
|
9782
|
-
const stream = await sendStreamingRequest(chatRequest, apiBaseUrl,
|
|
9768
|
+
const stream = await sendStreamingRequest(chatRequest, apiBaseUrl, organizationId);
|
|
9783
9769
|
const reader = stream.getReader();
|
|
9784
9770
|
const decoder = new TextDecoder();
|
|
9785
9771
|
while (true) {
|
|
@@ -9798,7 +9784,7 @@
|
|
|
9798
9784
|
let lastErr;
|
|
9799
9785
|
for (let attempt = 0; attempt <= STREAM_RETRY_DELAYS_MS.length; attempt += 1) {
|
|
9800
9786
|
try {
|
|
9801
|
-
const stream = await sendStreamingRequest(chatRequest, apiBaseUrl,
|
|
9787
|
+
const stream = await sendStreamingRequest(chatRequest, apiBaseUrl, organizationId);
|
|
9802
9788
|
await processStream(stream, setMessages);
|
|
9803
9789
|
updateActivity(organizationId);
|
|
9804
9790
|
return {
|
|
@@ -9826,7 +9812,7 @@
|
|
|
9826
9812
|
|
|
9827
9813
|
// Non-chat, non-onboarding apiType (shouldn't currently happen):
|
|
9828
9814
|
// fire and forget, single attempt.
|
|
9829
|
-
await sendStreamingRequest(chatRequest, apiBaseUrl,
|
|
9815
|
+
await sendStreamingRequest(chatRequest, apiBaseUrl, organizationId);
|
|
9830
9816
|
updateActivity(organizationId);
|
|
9831
9817
|
return {
|
|
9832
9818
|
success: true
|
|
@@ -9862,7 +9848,7 @@
|
|
|
9862
9848
|
// UX-M09: release the concurrency guard so the next send can proceed.
|
|
9863
9849
|
isSendingRef.current = false;
|
|
9864
9850
|
}
|
|
9865
|
-
}, [apiBaseUrl, apiKey, getNextMessageId,
|
|
9851
|
+
}, [apiBaseUrl, apiKey, getNextMessageId, organizationId, messages, bidiSessionId, routingChoice, widgetFilter, provideCitations]);
|
|
9866
9852
|
const handleOnboardingFormSubmit = reactExports.useCallback(async answers => {
|
|
9867
9853
|
const questions = getOnboardingQuestions(onboardingQuestions);
|
|
9868
9854
|
if (questions.length === 0) return;
|
|
@@ -91763,31 +91749,6 @@
|
|
|
91763
91749
|
return String(n);
|
|
91764
91750
|
}
|
|
91765
91751
|
|
|
91766
|
-
// ── Currency-aware value formatting ──────────────────────────────────────
|
|
91767
|
-
// A2UI chart data is normalized to `{ label, value }` by A2uiBlock's
|
|
91768
|
-
// numericData(), which drops the original Y-column key. A2uiBlock now threads
|
|
91769
|
-
// that key through as `props.yKey` so these renderers can decide whether the
|
|
91770
|
-
// axis / tooltip values are money. Heuristic mirrors the frontend's
|
|
91771
|
-
// chartShared.tsx `isCurrencyKey` — keep the keyword list in lockstep.
|
|
91772
|
-
// `(^|[_\W])` (not `\b`) so "average_salary" / "total_expenses" still match —
|
|
91773
|
-
// JS `\b` doesn't fire next to `_`.
|
|
91774
|
-
const CURRENCY_KEY_RE$1 = /(^|[_\W])(price|salary|cost|amount|revenue|expense|total|value|budget|spend|income|sale|balance)/i;
|
|
91775
|
-
function isCurrencyKey$1(key) {
|
|
91776
|
-
return typeof key === 'string' && CURRENCY_KEY_RE$1.test(key);
|
|
91777
|
-
}
|
|
91778
|
-
|
|
91779
|
-
// Compact currency for axis ticks → "$120.0k". Full currency for tooltips →
|
|
91780
|
-
// "$87,600" (comma-grouped, up to 2 decimals, no forced trailing zeros so a
|
|
91781
|
-
// whole-dollar salary reads "$81,200" not "$81,200.00").
|
|
91782
|
-
function formatCurrencyCompact(v) {
|
|
91783
|
-
return "$".concat(formatCompactNumber(v));
|
|
91784
|
-
}
|
|
91785
|
-
function formatCurrencyFull(v) {
|
|
91786
|
-
return "$".concat(Number(v).toLocaleString('en-US', {
|
|
91787
|
-
maximumFractionDigits: 2
|
|
91788
|
-
}));
|
|
91789
|
-
}
|
|
91790
|
-
|
|
91791
91752
|
/**
|
|
91792
91753
|
* Recharts bar chart — mirrors agent-sdk's eshalRenderers.BarChart.
|
|
91793
91754
|
*
|
|
@@ -91800,11 +91761,6 @@
|
|
|
91800
91761
|
isDark = false
|
|
91801
91762
|
} = _ref;
|
|
91802
91763
|
const data = Array.isArray(props.data) ? props.data : [];
|
|
91803
|
-
// Whether the Y values are money — drives the `$` on axis ticks + tooltip.
|
|
91804
|
-
const currency = isCurrencyKey$1(props.yKey);
|
|
91805
|
-
// Human series name for the tooltip ("Average Salary"), so it no longer
|
|
91806
|
-
// reads the generic dataKey "value".
|
|
91807
|
-
const seriesName = props.valueLabel || undefined;
|
|
91808
91764
|
const borderColor = isDark ? '#374151' : '#e2e8f0';
|
|
91809
91765
|
const bgColor = isDark ? '#1f2937' : '#ffffff';
|
|
91810
91766
|
const labelColor = isDark ? '#9ca3af' : '#64748b';
|
|
@@ -91879,9 +91835,6 @@
|
|
|
91879
91835
|
fontSize: 11,
|
|
91880
91836
|
fill: labelColor
|
|
91881
91837
|
},
|
|
91882
|
-
angle: 0,
|
|
91883
|
-
textAnchor: "middle",
|
|
91884
|
-
interval: 0,
|
|
91885
91838
|
label: props.xAxisLabel ? {
|
|
91886
91839
|
value: props.xAxisLabel,
|
|
91887
91840
|
position: 'insideBottom',
|
|
@@ -91902,9 +91855,9 @@
|
|
|
91902
91855
|
fontSize: 11,
|
|
91903
91856
|
fill: labelColor
|
|
91904
91857
|
} : undefined,
|
|
91905
|
-
tickFormatter: v =>
|
|
91858
|
+
tickFormatter: v => props.unit ? "".concat(formatCompactNumber(v), " ").concat(props.unit) : formatCompactNumber(v)
|
|
91906
91859
|
}), /*#__PURE__*/jsxRuntimeExports.jsx(Tooltip, {
|
|
91907
|
-
formatter: v =>
|
|
91860
|
+
formatter: v => props.unit ? "".concat(Number(v).toLocaleString(), " ").concat(props.unit) : Number(v).toLocaleString(),
|
|
91908
91861
|
contentStyle: {
|
|
91909
91862
|
background: tooltipBg,
|
|
91910
91863
|
border: "1px solid ".concat(borderColor),
|
|
@@ -91914,7 +91867,6 @@
|
|
|
91914
91867
|
}
|
|
91915
91868
|
}), /*#__PURE__*/jsxRuntimeExports.jsx(Bar, {
|
|
91916
91869
|
dataKey: "value",
|
|
91917
|
-
name: seriesName,
|
|
91918
91870
|
fill: "#3b82f6",
|
|
91919
91871
|
radius: [2, 2, 0, 0]
|
|
91920
91872
|
})]
|
|
@@ -91998,7 +91950,6 @@
|
|
|
91998
91950
|
donut = false
|
|
91999
91951
|
} = _ref3;
|
|
92000
91952
|
const data = Array.isArray(props.data) ? props.data : [];
|
|
92001
|
-
const currency = isCurrencyKey$1(props.yKey);
|
|
92002
91953
|
const borderColor = isDark ? '#374151' : '#e2e8f0';
|
|
92003
91954
|
const bgColor = isDark ? '#1f2937' : '#ffffff';
|
|
92004
91955
|
const titleColor = isDark ? '#f3f4f6' : '#0f172a';
|
|
@@ -92049,7 +92000,7 @@
|
|
|
92049
92000
|
fill: PIE_COLORS[i % PIE_COLORS.length]
|
|
92050
92001
|
}, i))
|
|
92051
92002
|
}), /*#__PURE__*/jsxRuntimeExports.jsx(Tooltip, {
|
|
92052
|
-
formatter: v =>
|
|
92003
|
+
formatter: v => Number(v).toLocaleString(),
|
|
92053
92004
|
contentStyle: {
|
|
92054
92005
|
background: tooltipBg,
|
|
92055
92006
|
border: "1px solid ".concat(borderColor),
|
|
@@ -92083,8 +92034,6 @@
|
|
|
92083
92034
|
area = false
|
|
92084
92035
|
} = _ref4;
|
|
92085
92036
|
const data = Array.isArray(props.data) ? props.data : [];
|
|
92086
|
-
const currency = isCurrencyKey$1(props.yKey);
|
|
92087
|
-
const seriesName = props.valueLabel || undefined;
|
|
92088
92037
|
const borderColor = isDark ? '#374151' : '#e2e8f0';
|
|
92089
92038
|
const bgColor = isDark ? '#1f2937' : '#ffffff';
|
|
92090
92039
|
const labelColor = isDark ? '#9ca3af' : '#64748b';
|
|
@@ -92160,9 +92109,6 @@
|
|
|
92160
92109
|
fontSize: 11,
|
|
92161
92110
|
fill: labelColor
|
|
92162
92111
|
},
|
|
92163
|
-
angle: 0,
|
|
92164
|
-
textAnchor: "middle",
|
|
92165
|
-
interval: 0,
|
|
92166
92112
|
label: props.xAxisLabel ? {
|
|
92167
92113
|
value: props.xAxisLabel,
|
|
92168
92114
|
position: 'insideBottom',
|
|
@@ -92176,9 +92122,9 @@
|
|
|
92176
92122
|
fontSize: 11,
|
|
92177
92123
|
fill: labelColor
|
|
92178
92124
|
},
|
|
92179
|
-
tickFormatter: v =>
|
|
92125
|
+
tickFormatter: v => props.unit ? "".concat(formatCompactNumber(v), " ").concat(props.unit) : formatCompactNumber(v)
|
|
92180
92126
|
}), /*#__PURE__*/jsxRuntimeExports.jsx(Tooltip, {
|
|
92181
|
-
formatter: v =>
|
|
92127
|
+
formatter: v => props.unit ? "".concat(Number(v).toLocaleString(), " ").concat(props.unit) : Number(v).toLocaleString(),
|
|
92182
92128
|
contentStyle: {
|
|
92183
92129
|
background: tooltipBg,
|
|
92184
92130
|
border: "1px solid ".concat(borderColor),
|
|
@@ -92189,7 +92135,6 @@
|
|
|
92189
92135
|
}), area ? /*#__PURE__*/jsxRuntimeExports.jsx(Area, {
|
|
92190
92136
|
type: "monotone",
|
|
92191
92137
|
dataKey: "value",
|
|
92192
|
-
name: seriesName,
|
|
92193
92138
|
stroke: "#3b82f6",
|
|
92194
92139
|
fill: "#3b82f6",
|
|
92195
92140
|
fillOpacity: 0.15,
|
|
@@ -92201,7 +92146,6 @@
|
|
|
92201
92146
|
}) : /*#__PURE__*/jsxRuntimeExports.jsx(Line, {
|
|
92202
92147
|
type: "monotone",
|
|
92203
92148
|
dataKey: "value",
|
|
92204
|
-
name: seriesName,
|
|
92205
92149
|
stroke: "#3b82f6",
|
|
92206
92150
|
strokeWidth: 2,
|
|
92207
92151
|
dot: {
|
|
@@ -92399,14 +92343,7 @@
|
|
|
92399
92343
|
data: rows,
|
|
92400
92344
|
unit: spec.unit,
|
|
92401
92345
|
xAxisLabel: spec.xAxisLabel,
|
|
92402
|
-
yAxisLabel: spec.yAxisLabel
|
|
92403
|
-
// numericData() collapses the rows to { label, value }, dropping the
|
|
92404
|
-
// original Y-column key. Pass it through so the renderer can detect a
|
|
92405
|
-
// currency column (→ "$" on the Y-axis + tooltip) and label the tooltip
|
|
92406
|
-
// series with the human column name ("Average Salary") instead of the
|
|
92407
|
-
// generic dataKey "value".
|
|
92408
|
-
yKey: y,
|
|
92409
|
-
valueLabel: prettifyLabel(y)
|
|
92346
|
+
yAxisLabel: spec.yAxisLabel
|
|
92410
92347
|
};
|
|
92411
92348
|
if (t === 'pie') return /*#__PURE__*/jsxRuntimeExports.jsx(PieChartRenderer, {
|
|
92412
92349
|
props: chartProps,
|
|
@@ -92733,12 +92670,10 @@
|
|
|
92733
92670
|
|
|
92734
92671
|
// ISO date / datetime prefix — matches the typical SQL output the
|
|
92735
92672
|
// agent's tools produce when GROUP BY-ing by month
|
|
92736
|
-
// (`DATE_TRUNC('month', col)` → `YYYY-MM-DD
|
|
92737
|
-
//
|
|
92738
|
-
//
|
|
92739
|
-
|
|
92740
|
-
// day-less variant fix).
|
|
92741
|
-
const ISO_DATE_RE = /^(\d{4})-(\d{2})(?:-\d{2})?(?:[T\s]|$)/;
|
|
92673
|
+
// (`DATE_TRUNC('month', col)` → `YYYY-MM-DD`). Used by formatAxisTick
|
|
92674
|
+
// below so a chart of monthly expenses shows "Jan '26" / "Feb '26"
|
|
92675
|
+
// horizontally instead of "2026 01 01" slanted (EV-1187 follow-up).
|
|
92676
|
+
const ISO_DATE_RE = /^(\d{4})-(\d{2})-(\d{2})(?:T|$)/;
|
|
92742
92677
|
function formatIsoDateAsMonthTick(value) {
|
|
92743
92678
|
if (typeof value !== 'string') return null;
|
|
92744
92679
|
const m = value.match(ISO_DATE_RE);
|
|
@@ -92781,14 +92716,6 @@
|
|
|
92781
92716
|
if (!SNAKE_CASE_CATEGORY_RE.test(value)) return value;
|
|
92782
92717
|
return prettifyLabel(value);
|
|
92783
92718
|
}
|
|
92784
|
-
|
|
92785
|
-
// Numeric string under a money column, e.g. "81,200" / "105444" / "-1,250.50".
|
|
92786
|
-
// Markdown tables (the common concierge path) arrive as text cells, so a
|
|
92787
|
-
// salary value is the STRING "81,200" — not a number — and skips the
|
|
92788
|
-
// USD_FORMATTER branch below. We prefix "$" while keeping the source's own
|
|
92789
|
-
// grouping/decimals so it reads "$81,200" (not "$81,200.00"). A leading "$"
|
|
92790
|
-
// already present makes the regex miss, so we never double-prefix.
|
|
92791
|
-
const _NUMERIC_STRING_RE = /^-?\d[\d,]*(\.\d+)?$/;
|
|
92792
92719
|
function formatCell(v, columnKey) {
|
|
92793
92720
|
if (v == null) return '';
|
|
92794
92721
|
if (typeof v === 'number') {
|
|
@@ -92798,11 +92725,6 @@
|
|
|
92798
92725
|
return Number.isInteger(v) ? String(v) : v.toFixed(2);
|
|
92799
92726
|
}
|
|
92800
92727
|
if (typeof v === 'object') return JSON.stringify(v);
|
|
92801
|
-
// Numeric string under a money-named column → prefix "$" (Bug: concierge
|
|
92802
|
-
// salary table showed "81,200" with no currency symbol).
|
|
92803
|
-
if (isCurrencyKey(columnKey) && _NUMERIC_STRING_RE.test(String(v).trim())) {
|
|
92804
|
-
return "$".concat(String(v).trim());
|
|
92805
|
-
}
|
|
92806
92728
|
// String cells: prettify snake_case category labels (e.g.
|
|
92807
92729
|
// `office_supplies` → "Office Supplies") so the table reads
|
|
92808
92730
|
// consistently with the chart's X-axis ticks on the same data.
|
|
@@ -94380,7 +94302,7 @@
|
|
|
94380
94302
|
let {
|
|
94381
94303
|
sources = [],
|
|
94382
94304
|
apiBaseUrl,
|
|
94383
|
-
|
|
94305
|
+
organizationId,
|
|
94384
94306
|
isDark,
|
|
94385
94307
|
textColor,
|
|
94386
94308
|
fontFamily,
|
|
@@ -94406,7 +94328,7 @@
|
|
|
94406
94328
|
// corpora that have none of the above.
|
|
94407
94329
|
const _downloadKey = s => s.url || s.source_id || s.source_name || "";
|
|
94408
94330
|
const handleFileDownload = async source => {
|
|
94409
|
-
if (!apiBaseUrl) return;
|
|
94331
|
+
if (!apiBaseUrl || !organizationId) return;
|
|
94410
94332
|
// SharePoint proxy URLs are GET endpoints that stream the file
|
|
94411
94333
|
// directly via Microsoft Graph using the stored OAuth credential.
|
|
94412
94334
|
// Bypass the legacy POST /file-download lookup (which expects an
|
|
@@ -94432,14 +94354,11 @@
|
|
|
94432
94354
|
// set, or future deployments that set it) pass through
|
|
94433
94355
|
// unchanged.
|
|
94434
94356
|
const spUrl = source.url.startsWith("http") ? source.url : "".concat(apiBaseUrl.replace(/\/$/, "")).concat(source.url);
|
|
94435
|
-
const spHeaders = {};
|
|
94436
|
-
if (typeof getAuthToken === 'function') {
|
|
94437
|
-
const token = await getAuthToken();
|
|
94438
|
-
if (token) spHeaders['Authorization'] = "Bearer ".concat(token);
|
|
94439
|
-
}
|
|
94440
94357
|
res = await fetch(spUrl, {
|
|
94441
94358
|
method: "GET",
|
|
94442
|
-
headers:
|
|
94359
|
+
headers: {
|
|
94360
|
+
"x-eshal-org": organizationId
|
|
94361
|
+
},
|
|
94443
94362
|
credentials: "include"
|
|
94444
94363
|
});
|
|
94445
94364
|
} else {
|
|
@@ -94455,18 +94374,15 @@
|
|
|
94455
94374
|
// - source.source_name (the user's original filename)
|
|
94456
94375
|
// Sending both lets the server skip the Milvus resolution
|
|
94457
94376
|
// AND advertise the readable name in Content-Disposition.
|
|
94458
|
-
const dlHeaders = {
|
|
94459
|
-
"Content-Type": "application/json"
|
|
94460
|
-
};
|
|
94461
|
-
if (typeof getAuthToken === 'function') {
|
|
94462
|
-
const token = await getAuthToken();
|
|
94463
|
-
if (token) dlHeaders['Authorization'] = "Bearer ".concat(token);
|
|
94464
|
-
}
|
|
94465
94377
|
res = await fetch(url, {
|
|
94466
94378
|
method: "POST",
|
|
94467
|
-
headers:
|
|
94379
|
+
headers: {
|
|
94380
|
+
"Content-Type": "application/json",
|
|
94381
|
+
"x-eshal-org": organizationId
|
|
94382
|
+
},
|
|
94468
94383
|
credentials: "include",
|
|
94469
94384
|
body: JSON.stringify({
|
|
94385
|
+
org_id: organizationId,
|
|
94470
94386
|
source_id: source.source_id,
|
|
94471
94387
|
file_name: source.url || "",
|
|
94472
94388
|
source_name: source.source_name || ""
|
|
@@ -94651,7 +94567,7 @@
|
|
|
94651
94567
|
conciergeName,
|
|
94652
94568
|
companyName,
|
|
94653
94569
|
apiBaseUrl,
|
|
94654
|
-
|
|
94570
|
+
organizationId,
|
|
94655
94571
|
onPromptSuggestionClick,
|
|
94656
94572
|
allowFeedback = true,
|
|
94657
94573
|
provideCitations = true,
|
|
@@ -94823,7 +94739,7 @@
|
|
|
94823
94739
|
}), /*#__PURE__*/jsxRuntimeExports.jsx(MessageSources, {
|
|
94824
94740
|
sources: message.sources,
|
|
94825
94741
|
apiBaseUrl: apiBaseUrl,
|
|
94826
|
-
|
|
94742
|
+
organizationId: organizationId,
|
|
94827
94743
|
isDark: isDark,
|
|
94828
94744
|
textColor: messageTextColor,
|
|
94829
94745
|
fontFamily: messageFontFamily,
|
|
@@ -94849,7 +94765,7 @@
|
|
|
94849
94765
|
}), /*#__PURE__*/jsxRuntimeExports.jsx(MessageSources, {
|
|
94850
94766
|
sources: message.sources,
|
|
94851
94767
|
apiBaseUrl: apiBaseUrl,
|
|
94852
|
-
|
|
94768
|
+
organizationId: organizationId,
|
|
94853
94769
|
isDark: isDark,
|
|
94854
94770
|
textColor: messageTextColor,
|
|
94855
94771
|
fontFamily: messageFontFamily,
|
|
@@ -95029,7 +94945,7 @@
|
|
|
95029
94945
|
conciergeName,
|
|
95030
94946
|
companyName,
|
|
95031
94947
|
apiBaseUrl,
|
|
95032
|
-
|
|
94948
|
+
organizationId,
|
|
95033
94949
|
onPromptSuggestionClick,
|
|
95034
94950
|
allowFeedback,
|
|
95035
94951
|
provideCitations,
|
|
@@ -95073,7 +94989,7 @@
|
|
|
95073
94989
|
conciergeName: conciergeName,
|
|
95074
94990
|
companyName: companyName,
|
|
95075
94991
|
apiBaseUrl: apiBaseUrl,
|
|
95076
|
-
|
|
94992
|
+
organizationId: organizationId,
|
|
95077
94993
|
onPromptSuggestionClick: onPromptSuggestionClick,
|
|
95078
94994
|
allowFeedback: allowFeedback,
|
|
95079
94995
|
provideCitations: provideCitations,
|
|
@@ -95918,7 +95834,7 @@
|
|
|
95918
95834
|
conciergeName,
|
|
95919
95835
|
companyName,
|
|
95920
95836
|
apiBaseUrl,
|
|
95921
|
-
|
|
95837
|
+
organizationId,
|
|
95922
95838
|
onPromptSuggestionClick,
|
|
95923
95839
|
conversationResetKey,
|
|
95924
95840
|
allowFeedback,
|
|
@@ -96100,7 +96016,7 @@
|
|
|
96100
96016
|
conciergeName: conciergeName,
|
|
96101
96017
|
companyName: companyName,
|
|
96102
96018
|
apiBaseUrl: apiBaseUrl,
|
|
96103
|
-
|
|
96019
|
+
organizationId: organizationId,
|
|
96104
96020
|
allowFeedback: allowFeedback,
|
|
96105
96021
|
provideCitations: provideCitations,
|
|
96106
96022
|
onFeedback: onFeedback,
|
|
@@ -96132,7 +96048,7 @@
|
|
|
96132
96048
|
conciergeName: conciergeName,
|
|
96133
96049
|
companyName: companyName,
|
|
96134
96050
|
apiBaseUrl: apiBaseUrl,
|
|
96135
|
-
|
|
96051
|
+
organizationId: organizationId,
|
|
96136
96052
|
headerTextColor: headerTextColor
|
|
96137
96053
|
}, "skeleton-waiting");
|
|
96138
96054
|
}
|
|
@@ -96247,18 +96163,7 @@
|
|
|
96247
96163
|
copyChat: "Copy Chat",
|
|
96248
96164
|
startChat: "Start Chat",
|
|
96249
96165
|
startingVoiceSession: "Starting voice session...",
|
|
96250
|
-
allowMicAccess: "Allow microphone access"
|
|
96251
|
-
// Onboarding-form validation messages. These are the English fallback for
|
|
96252
|
-
// every locale (translate() falls back to EN[key]); without them the form
|
|
96253
|
-
// rendered the raw key strings ("emailInvalid", etc.). `fieldRequiredSuffix`
|
|
96254
|
-
// is appended after a field label: `${label} ${fieldRequiredSuffix}`.
|
|
96255
|
-
emailRequired: "Please enter your email address",
|
|
96256
|
-
nameRequired: "Please enter your name",
|
|
96257
|
-
phoneRequired: "Please enter your phone number",
|
|
96258
|
-
fieldRequiredSuffix: "is required",
|
|
96259
|
-
emailInvalid: "Please enter a valid email address",
|
|
96260
|
-
nameInvalid: "Please enter a valid name",
|
|
96261
|
-
phoneInvalid: "Please enter a valid phone number"
|
|
96166
|
+
allowMicAccess: "Allow microphone access"
|
|
96262
96167
|
};
|
|
96263
96168
|
const DICTIONARIES = {
|
|
96264
96169
|
en: EN,
|
|
@@ -99647,7 +99552,7 @@
|
|
|
99647
99552
|
showPoweredBy = true,
|
|
99648
99553
|
apiBaseUrl,
|
|
99649
99554
|
apiKey,
|
|
99650
|
-
|
|
99555
|
+
organizationId,
|
|
99651
99556
|
conversationId,
|
|
99652
99557
|
conciergeId,
|
|
99653
99558
|
onboardingQuestions = [],
|
|
@@ -99795,7 +99700,7 @@
|
|
|
99795
99700
|
setCsatSubmitted(submitted);
|
|
99796
99701
|
if (submitted) setShowCsat(false);
|
|
99797
99702
|
} catch (_unused4) {}
|
|
99798
|
-
}, [conversationId]);
|
|
99703
|
+
}, [conversationId, organizationId]);
|
|
99799
99704
|
|
|
99800
99705
|
// Auto-scroll hook
|
|
99801
99706
|
useAutoScroll(messagesContainerRef, [messages, isLoading], {
|
|
@@ -99933,15 +99838,14 @@
|
|
|
99933
99838
|
const handleCsatRating = async (rating, format) => {
|
|
99934
99839
|
setCsatSubmitted(true); // optimistic UI
|
|
99935
99840
|
setShowCsat(false);
|
|
99936
|
-
markCsatSubmitted(
|
|
99841
|
+
markCsatSubmitted(organizationId); // persist so page refresh doesn't re-show CSAT
|
|
99937
99842
|
|
|
99938
99843
|
try {
|
|
99939
99844
|
const headers = {
|
|
99940
99845
|
"Content-Type": "application/json"
|
|
99941
99846
|
};
|
|
99942
|
-
if (
|
|
99943
|
-
|
|
99944
|
-
if (token) headers['Authorization'] = "Bearer ".concat(token);
|
|
99847
|
+
if (organizationId) {
|
|
99848
|
+
headers["x-eshal-org"] = organizationId;
|
|
99945
99849
|
}
|
|
99946
99850
|
const response = await fetch("".concat(apiBaseUrl.replace(/\/$/, ""), "/api/v1/support/csat"), {
|
|
99947
99851
|
method: "POST",
|
|
@@ -99951,6 +99855,7 @@
|
|
|
99951
99855
|
conversationId,
|
|
99952
99856
|
rating,
|
|
99953
99857
|
ratingFormat: format,
|
|
99858
|
+
organizationId,
|
|
99954
99859
|
conciergeId
|
|
99955
99860
|
})
|
|
99956
99861
|
});
|
|
@@ -99974,9 +99879,8 @@
|
|
|
99974
99879
|
const headers = {
|
|
99975
99880
|
"Content-Type": "application/json"
|
|
99976
99881
|
};
|
|
99977
|
-
if (
|
|
99978
|
-
|
|
99979
|
-
if (token) headers['Authorization'] = "Bearer ".concat(token);
|
|
99882
|
+
if (organizationId) {
|
|
99883
|
+
headers["x-eshal-org"] = organizationId;
|
|
99980
99884
|
}
|
|
99981
99885
|
const ratingFormat = normalizeCsatFormat(csatFormat);
|
|
99982
99886
|
const response = await fetch("".concat(apiBaseUrl.replace(/\/$/, ""), "/api/v1/support/csat"), {
|
|
@@ -99987,6 +99891,7 @@
|
|
|
99987
99891
|
conversationId,
|
|
99988
99892
|
rating: String(resolveRating),
|
|
99989
99893
|
ratingFormat,
|
|
99894
|
+
organizationId,
|
|
99990
99895
|
conciergeId,
|
|
99991
99896
|
feedback: resolveFeedbackText || undefined
|
|
99992
99897
|
})
|
|
@@ -100020,7 +99925,7 @@
|
|
|
100020
99925
|
} finally {
|
|
100021
99926
|
setIsSubmittingResolve(false);
|
|
100022
99927
|
}
|
|
100023
|
-
}, [isCsatEnabled, isSubmittingResolve, isConversationResolved, resolveRating, resolveFeedbackText, conversationId,
|
|
99928
|
+
}, [isCsatEnabled, isSubmittingResolve, isConversationResolved, resolveRating, resolveFeedbackText, conversationId, organizationId, conciergeId, apiBaseUrl, csatFormat, onResetConversation, onRefreshChat, onWidgetFilterChange]);
|
|
100024
99929
|
|
|
100025
99930
|
// Handle X button: first click shows form, second click closes
|
|
100026
99931
|
const handleCloseClick = reactExports.useCallback(() => {
|
|
@@ -100358,7 +100263,7 @@
|
|
|
100358
100263
|
conciergeName: conciergeName,
|
|
100359
100264
|
companyName: companyName,
|
|
100360
100265
|
apiBaseUrl: apiBaseUrl,
|
|
100361
|
-
|
|
100266
|
+
organizationId: organizationId,
|
|
100362
100267
|
conversationResetKey: conversationId,
|
|
100363
100268
|
allowFeedback: allowFeedback,
|
|
100364
100269
|
provideCitations: provideCitations,
|
|
@@ -100443,7 +100348,7 @@
|
|
|
100443
100348
|
|
|
100444
100349
|
/**
|
|
100445
100350
|
* @param {string|undefined|null} apiBaseUrl
|
|
100446
|
-
* @param {
|
|
100351
|
+
* @param {string|undefined|null} orgId
|
|
100447
100352
|
* @returns {{
|
|
100448
100353
|
* categories: Array<{id:string,label:string,count:number,tags:string[]}>,
|
|
100449
100354
|
* loading: boolean,
|
|
@@ -100451,15 +100356,14 @@
|
|
|
100451
100356
|
* hasFilters: boolean,
|
|
100452
100357
|
* }}
|
|
100453
100358
|
*/
|
|
100454
|
-
function useFilterCatalog(apiBaseUrl,
|
|
100455
|
-
const hasAuth = !!apiBaseUrl;
|
|
100359
|
+
function useFilterCatalog(apiBaseUrl, orgId) {
|
|
100456
100360
|
const [state, setState] = reactExports.useState({
|
|
100457
100361
|
categories: [],
|
|
100458
|
-
loading:
|
|
100362
|
+
loading: !!(apiBaseUrl && orgId),
|
|
100459
100363
|
error: null
|
|
100460
100364
|
});
|
|
100461
100365
|
reactExports.useEffect(() => {
|
|
100462
|
-
if (!apiBaseUrl) {
|
|
100366
|
+
if (!apiBaseUrl || !orgId) {
|
|
100463
100367
|
setState({
|
|
100464
100368
|
categories: [],
|
|
100465
100369
|
loading: false,
|
|
@@ -100467,8 +100371,8 @@
|
|
|
100467
100371
|
});
|
|
100468
100372
|
return;
|
|
100469
100373
|
}
|
|
100470
|
-
const
|
|
100471
|
-
const cached = _PROCESS_CACHE.get(
|
|
100374
|
+
const key = "".concat(apiBaseUrl, "|").concat(orgId);
|
|
100375
|
+
const cached = _PROCESS_CACHE.get(key);
|
|
100472
100376
|
if (cached) {
|
|
100473
100377
|
setState({
|
|
100474
100378
|
categories: cached,
|
|
@@ -100482,55 +100386,43 @@
|
|
|
100482
100386
|
loading: true,
|
|
100483
100387
|
error: null
|
|
100484
100388
|
}));
|
|
100485
|
-
const
|
|
100486
|
-
|
|
100487
|
-
|
|
100488
|
-
|
|
100489
|
-
|
|
100490
|
-
Accept: "application/json"
|
|
100491
|
-
};
|
|
100492
|
-
// In-app JWT path: set Authorization header
|
|
100493
|
-
// Mode B proxy: no auth header; proxy handles org resolution
|
|
100494
|
-
if (typeof getAuthToken === 'function') {
|
|
100495
|
-
const token = await getAuthToken();
|
|
100496
|
-
if (token) {
|
|
100497
|
-
headers['Authorization'] = "Bearer ".concat(token);
|
|
100498
|
-
}
|
|
100499
|
-
}
|
|
100500
|
-
const res = await fetch(url, {
|
|
100501
|
-
method: "GET",
|
|
100502
|
-
headers
|
|
100503
|
-
});
|
|
100504
|
-
if (!res.ok) {
|
|
100505
|
-
throw new Error("filter catalog HTTP ".concat(res.status));
|
|
100506
|
-
}
|
|
100507
|
-
const body = await res.json();
|
|
100508
|
-
const categories = Array.isArray(body === null || body === void 0 ? void 0 : body.categories) ? body.categories : [];
|
|
100509
|
-
if (cancelled) return;
|
|
100510
|
-
_PROCESS_CACHE.set(cacheKey, categories);
|
|
100511
|
-
setState({
|
|
100512
|
-
categories,
|
|
100513
|
-
loading: false,
|
|
100514
|
-
error: null
|
|
100515
|
-
});
|
|
100516
|
-
} catch (err) {
|
|
100517
|
-
if (cancelled) return;
|
|
100518
|
-
// Fail-soft: log and present empty so the UI hides itself.
|
|
100519
|
-
// We do NOT bubble the error to the caller because chat
|
|
100520
|
-
// should keep working even if the catalog is unreachable.
|
|
100521
|
-
// eslint-disable-next-line no-console
|
|
100522
|
-
console.warn("[useFilterCatalog] catalog fetch failed:", err);
|
|
100523
|
-
setState({
|
|
100524
|
-
categories: [],
|
|
100525
|
-
loading: false,
|
|
100526
|
-
error: err
|
|
100527
|
-
});
|
|
100389
|
+
const url = "".concat(apiBaseUrl.replace(/\/$/, ""), "/api/v1/widget/filters/").concat(encodeURIComponent(orgId));
|
|
100390
|
+
fetch(url, {
|
|
100391
|
+
method: "GET",
|
|
100392
|
+
headers: {
|
|
100393
|
+
Accept: "application/json"
|
|
100528
100394
|
}
|
|
100529
|
-
})(
|
|
100395
|
+
}).then(async res => {
|
|
100396
|
+
if (!res.ok) {
|
|
100397
|
+
throw new Error("filter catalog HTTP ".concat(res.status));
|
|
100398
|
+
}
|
|
100399
|
+
const body = await res.json();
|
|
100400
|
+
return Array.isArray(body === null || body === void 0 ? void 0 : body.categories) ? body.categories : [];
|
|
100401
|
+
}).then(categories => {
|
|
100402
|
+
if (cancelled) return;
|
|
100403
|
+
_PROCESS_CACHE.set(key, categories);
|
|
100404
|
+
setState({
|
|
100405
|
+
categories,
|
|
100406
|
+
loading: false,
|
|
100407
|
+
error: null
|
|
100408
|
+
});
|
|
100409
|
+
}).catch(err => {
|
|
100410
|
+
if (cancelled) return;
|
|
100411
|
+
// Fail-soft: log and present empty so the UI hides itself.
|
|
100412
|
+
// We do NOT bubble the error to the caller because chat
|
|
100413
|
+
// should keep working even if the catalog is unreachable.
|
|
100414
|
+
// eslint-disable-next-line no-console
|
|
100415
|
+
console.warn("[useFilterCatalog] catalog fetch failed:", err);
|
|
100416
|
+
setState({
|
|
100417
|
+
categories: [],
|
|
100418
|
+
loading: false,
|
|
100419
|
+
error: err
|
|
100420
|
+
});
|
|
100421
|
+
});
|
|
100530
100422
|
return () => {
|
|
100531
100423
|
cancelled = true;
|
|
100532
100424
|
};
|
|
100533
|
-
}, [apiBaseUrl,
|
|
100425
|
+
}, [apiBaseUrl, orgId]);
|
|
100534
100426
|
const hasFilters = reactExports.useMemo(() => Array.isArray(state.categories) && state.categories.length > 0, [state.categories]);
|
|
100535
100427
|
return _objectSpread2(_objectSpread2({}, state), {}, {
|
|
100536
100428
|
hasFilters
|
|
@@ -100609,31 +100501,20 @@
|
|
|
100609
100501
|
/**
|
|
100610
100502
|
* Fetches agent configuration from the deploy-agent endpoint
|
|
100611
100503
|
* @param {string} apiBaseUrl - Base URL for the API
|
|
100612
|
-
* @param {
|
|
100504
|
+
* @param {string} orgId - Organization ID
|
|
100613
100505
|
* @returns {Promise<object>} Agent configuration object
|
|
100614
100506
|
*/
|
|
100615
|
-
const fetchAgentConfig = async (apiBaseUrl,
|
|
100616
|
-
if (!apiBaseUrl) {
|
|
100617
|
-
throw new Error("apiBaseUrl
|
|
100507
|
+
const fetchAgentConfig = async (apiBaseUrl, orgId) => {
|
|
100508
|
+
if (!apiBaseUrl || !orgId) {
|
|
100509
|
+
throw new Error("apiBaseUrl and orgId are required");
|
|
100618
100510
|
}
|
|
100619
100511
|
try {
|
|
100620
|
-
const
|
|
100621
|
-
const url = "".concat(base, "/api/v1/deploy-agent");
|
|
100622
|
-
const headers = {
|
|
100623
|
-
"Content-Type": "application/json"
|
|
100624
|
-
};
|
|
100625
|
-
|
|
100626
|
-
// In-app JWT path: set Authorization header
|
|
100627
|
-
// Mode B proxy: no auth header; proxy handles org resolution
|
|
100628
|
-
if (typeof getAuthToken === 'function') {
|
|
100629
|
-
const token = await getAuthToken();
|
|
100630
|
-
if (token) {
|
|
100631
|
-
headers['Authorization'] = "Bearer ".concat(token);
|
|
100632
|
-
}
|
|
100633
|
-
}
|
|
100512
|
+
const url = "".concat(apiBaseUrl.replace(/\/$/, ''), "/api/v1/deploy-agent/").concat(orgId);
|
|
100634
100513
|
const response = await fetch(url, {
|
|
100635
100514
|
method: "GET",
|
|
100636
|
-
headers
|
|
100515
|
+
headers: {
|
|
100516
|
+
"Content-Type": "application/json"
|
|
100517
|
+
},
|
|
100637
100518
|
credentials: "include"
|
|
100638
100519
|
});
|
|
100639
100520
|
if (!response.ok) {
|
|
@@ -100650,10 +100531,8 @@
|
|
|
100650
100531
|
const ChatWidget = _ref => {
|
|
100651
100532
|
var _agentConfig$concierg;
|
|
100652
100533
|
let {
|
|
100653
|
-
|
|
100654
|
-
|
|
100655
|
-
*/
|
|
100656
|
-
getAuthToken,
|
|
100534
|
+
// Required props for dynamic config
|
|
100535
|
+
orgId,
|
|
100657
100536
|
apiBaseUrl,
|
|
100658
100537
|
// Optional override props (will override fetched config if provided)
|
|
100659
100538
|
darkMode,
|
|
@@ -100695,8 +100574,7 @@
|
|
|
100695
100574
|
onboardingQuestions,
|
|
100696
100575
|
collectionPrompt,
|
|
100697
100576
|
allowedDomains,
|
|
100698
|
-
onboardingEnabled
|
|
100699
|
-
wsBaseUrl
|
|
100577
|
+
onboardingEnabled
|
|
100700
100578
|
} = _ref;
|
|
100701
100579
|
const [agentConfig, setAgentConfig] = reactExports.useState(null);
|
|
100702
100580
|
const [isLoadingConfig, setIsLoadingConfig] = reactExports.useState(true);
|
|
@@ -100705,14 +100583,14 @@
|
|
|
100705
100583
|
// Fetch agent configuration on mount
|
|
100706
100584
|
reactExports.useEffect(() => {
|
|
100707
100585
|
const loadAgentConfig = async () => {
|
|
100708
|
-
if (!apiBaseUrl) {
|
|
100709
|
-
setConfigError("apiBaseUrl
|
|
100586
|
+
if (!orgId || !apiBaseUrl) {
|
|
100587
|
+
setConfigError("orgId and apiBaseUrl are required");
|
|
100710
100588
|
setIsLoadingConfig(false);
|
|
100711
100589
|
return;
|
|
100712
100590
|
}
|
|
100713
100591
|
try {
|
|
100714
100592
|
setIsLoadingConfig(true);
|
|
100715
|
-
const config = await fetchAgentConfig(apiBaseUrl,
|
|
100593
|
+
const config = await fetchAgentConfig(apiBaseUrl, orgId);
|
|
100716
100594
|
setAgentConfig(config);
|
|
100717
100595
|
setConfigError(null);
|
|
100718
100596
|
} catch (error) {
|
|
@@ -100723,11 +100601,11 @@
|
|
|
100723
100601
|
}
|
|
100724
100602
|
};
|
|
100725
100603
|
loadAgentConfig();
|
|
100726
|
-
}, [
|
|
100604
|
+
}, [orgId, apiBaseUrl]);
|
|
100727
100605
|
|
|
100728
100606
|
// Map API response to widget props (use props as overrides if provided)
|
|
100729
100607
|
const widgetConfig = reactExports.useMemo(() => {
|
|
100730
|
-
var _agentConfig$config, _cfg$identity, _cfg$branding, _cfg$typography, _cfg$messages, _cfg$voice, _cfg$onboarding, _cfg$disclaimer, _cfg$csat, _cfg$inactivity, _cfg$behavior, _cfg$security, _cfg$layout, _ref2, _messages$bundles$loc, _messages$bundles, _messages$bundles$en, _messages$bundles2, _ref3, _ref4, _ref5, _ref6, _branding$chatbotAvat, _branding$chatbotAvat2, _ref7, _ref8, _ref9, _ref0, _ref1, _ref10, _ref11, _ref12, _typography$headerTex, _ref13, _ref14, _ref15, _ref16, _ref17, _ref18, _inactivity$timeoutVa, _inactivity$timeoutUn, _ref19, _ref20, _ref21, _ref22, _ref23, _ref24, _ref25, _ref26, _csat$enabled, _csat$format, _csat$prompt, _csat$triggerType, _csat$idleTimeoutMins, _csat$followUpEnabled, _csat$followUpPrompt, _behavior$autoPromptE, _behavior$allowFeedba, _behavior$provideCita
|
|
100608
|
+
var _agentConfig$config, _cfg$identity, _cfg$branding, _cfg$typography, _cfg$messages, _cfg$voice, _cfg$onboarding, _cfg$disclaimer, _cfg$csat, _cfg$inactivity, _cfg$behavior, _cfg$security, _cfg$layout, _ref2, _messages$bundles$loc, _messages$bundles, _messages$bundles$en, _messages$bundles2, _ref3, _ref4, _ref5, _ref6, _branding$chatbotAvat, _branding$chatbotAvat2, _ref7, _ref8, _ref9, _ref0, _ref1, _ref10, _ref11, _ref12, _typography$headerTex, _ref13, _ref14, _ref15, _ref16, _ref17, _ref18, _inactivity$timeoutVa, _inactivity$timeoutUn, _ref19, _ref20, _ref21, _ref22, _ref23, _ref24, _ref25, _ref26, _csat$enabled, _csat$format, _csat$prompt, _csat$triggerType, _csat$idleTimeoutMins, _csat$followUpEnabled, _csat$followUpPrompt, _behavior$autoPromptE, _behavior$allowFeedba, _behavior$provideCita;
|
|
100731
100609
|
if (!agentConfig) {
|
|
100732
100610
|
return null;
|
|
100733
100611
|
}
|
|
@@ -100786,6 +100664,7 @@
|
|
|
100786
100664
|
fontFamily: (_ref1 = fontFamily !== null && fontFamily !== void 0 ? fontFamily : typography.agentMessageBubbleFontFamily) !== null && _ref1 !== void 0 ? _ref1 : "Inter",
|
|
100787
100665
|
fontSize: (_ref10 = fontSize !== null && fontSize !== void 0 ? fontSize : typography.agentMessageBubbleFontSize) !== null && _ref10 !== void 0 ? _ref10 : "14px",
|
|
100788
100666
|
defaultLanguage: locale,
|
|
100667
|
+
organizationId: orgId,
|
|
100789
100668
|
headerTextBold: (_ref11 = headerTextBold !== null && headerTextBold !== void 0 ? headerTextBold : typography.headerTextBold) !== null && _ref11 !== void 0 ? _ref11 : false,
|
|
100790
100669
|
headerTextItalic: (_ref12 = headerTextItalic !== null && headerTextItalic !== void 0 ? headerTextItalic : typography.headerTextItalic) !== null && _ref12 !== void 0 ? _ref12 : false,
|
|
100791
100670
|
headerTextColor: (_typography$headerTex = typography.headerTextColor) !== null && _typography$headerTex !== void 0 ? _typography$headerTex : "#FFFFFF",
|
|
@@ -100822,12 +100701,9 @@
|
|
|
100822
100701
|
// every message and tell the backend to skip citation generation.
|
|
100823
100702
|
// Defaults to true so agents that haven't set the field keep showing
|
|
100824
100703
|
// citations (matches the testing-lab / chatbot-preview default).
|
|
100825
|
-
provideCitations: (_behavior$provideCita = behavior.provideCitations) !== null && _behavior$provideCita !== void 0 ? _behavior$provideCita : true
|
|
100826
|
-
// organizationId from the backend response — used internally for
|
|
100827
|
-
// session isolation (localStorage keys) and WS paths. Not a user prop.
|
|
100828
|
-
organizationId: (_agentConfig$organiza = agentConfig.organizationId) !== null && _agentConfig$organiza !== void 0 ? _agentConfig$organiza : null
|
|
100704
|
+
provideCitations: (_behavior$provideCita = behavior.provideCitations) !== null && _behavior$provideCita !== void 0 ? _behavior$provideCita : true
|
|
100829
100705
|
};
|
|
100830
|
-
}, [agentConfig, darkMode, primaryColor, position, companyName, conciergeName, companyLogo, welcomeMessage, quickQuestions, quickQuestionsEnabled, textColor, agentMessageBubbleColor, userMessageBoxColor, assistantTextColor, userTextColor, fontFamily, fontSize, defaultLanguage, headerTextBold, headerTextItalic, enableVoiceInteraction, disclaimerEnabled, disclaimerText, disclaimerPosition, disclaimerLinkLabel, disclaimerLinkUrl, showPoweredBy, onboardingQuestions, onboardingEnabled, collectionPrompt, allowedDomains, agentMessageBubbleFontFamily, agentMessageBubbleFontSize, userMessageBubbleFontFamily, userMessageBubbleFontSize
|
|
100706
|
+
}, [agentConfig, darkMode, primaryColor, position, companyName, conciergeName, companyLogo, welcomeMessage, quickQuestions, quickQuestionsEnabled, textColor, agentMessageBubbleColor, userMessageBoxColor, assistantTextColor, userTextColor, fontFamily, fontSize, defaultLanguage, orgId, headerTextBold, headerTextItalic, enableVoiceInteraction, disclaimerEnabled, disclaimerText, disclaimerPosition, disclaimerLinkLabel, disclaimerLinkUrl, showPoweredBy, onboardingQuestions, onboardingEnabled, collectionPrompt, allowedDomains, agentMessageBubbleFontFamily, agentMessageBubbleFontSize, userMessageBubbleFontFamily, userMessageBubbleFontSize
|
|
100831
100707
|
// CSAT dependencies
|
|
100832
100708
|
// (kept minimal because these come from agentConfig)
|
|
100833
100709
|
]);
|
|
@@ -100846,7 +100722,7 @@
|
|
|
100846
100722
|
quickQuestions: [],
|
|
100847
100723
|
apiBaseUrl: apiBaseUrl || "",
|
|
100848
100724
|
apiKey: apiKey || "",
|
|
100849
|
-
|
|
100725
|
+
organizationId: orgId || "",
|
|
100850
100726
|
autoOpen: false,
|
|
100851
100727
|
openDelay: 0,
|
|
100852
100728
|
darkMode: false,
|
|
@@ -100854,7 +100730,7 @@
|
|
|
100854
100730
|
onboardingQuestions: [],
|
|
100855
100731
|
onboardingEnabled: false,
|
|
100856
100732
|
collectionPrompt: undefined
|
|
100857
|
-
}), [apiBaseUrl, apiKey,
|
|
100733
|
+
}), [apiBaseUrl, apiKey, orgId]);
|
|
100858
100734
|
|
|
100859
100735
|
// ALWAYS call hooks before any early returns (Rules of Hooks)
|
|
100860
100736
|
const {
|
|
@@ -100896,10 +100772,13 @@
|
|
|
100896
100772
|
welcomeMessage: widgetConfig.welcomeMessage,
|
|
100897
100773
|
quickQuestions: widgetConfig.quickQuestions,
|
|
100898
100774
|
apiBaseUrl,
|
|
100899
|
-
|
|
100775
|
+
// Voice WS endpoint is derived entirely from apiBaseUrl inside
|
|
100776
|
+
// useChatState (→ wss://{host}/voice/ws, the frontend voice proxy).
|
|
100777
|
+
// Nothing is forwarded/overridable here, so a stale backend or embed
|
|
100778
|
+
// wsBaseUrl can't point voice at the old (now non-public)
|
|
100779
|
+
// knowledge-api LoadBalancer.
|
|
100900
100780
|
apiKey,
|
|
100901
100781
|
organizationId: widgetConfig.organizationId,
|
|
100902
|
-
getAuthToken,
|
|
100903
100782
|
autoOpen,
|
|
100904
100783
|
openDelay,
|
|
100905
100784
|
darkMode: widgetConfig.darkMode,
|
|
@@ -100926,33 +100805,24 @@
|
|
|
100926
100805
|
// navigation (but not a page refresh — feedback is not written to
|
|
100927
100806
|
// localStorage so it resets on reload as intended).
|
|
100928
100807
|
updateMessageFeedback(messageContent, feedback);
|
|
100929
|
-
if (!conversationId || !apiBaseUrl) return;
|
|
100808
|
+
if (!orgId || !conversationId || !apiBaseUrl) return;
|
|
100930
100809
|
const base = apiBaseUrl.replace(/\/$/, "");
|
|
100931
|
-
(
|
|
100932
|
-
|
|
100933
|
-
|
|
100934
|
-
|
|
100935
|
-
|
|
100936
|
-
|
|
100937
|
-
|
|
100938
|
-
|
|
100939
|
-
|
|
100940
|
-
|
|
100941
|
-
|
|
100942
|
-
|
|
100943
|
-
|
|
100944
|
-
|
|
100945
|
-
|
|
100946
|
-
|
|
100947
|
-
feedback,
|
|
100948
|
-
feedbackText
|
|
100949
|
-
})
|
|
100950
|
-
});
|
|
100951
|
-
} catch (err) {
|
|
100952
|
-
console.error("Feedback submission error:", err);
|
|
100953
|
-
}
|
|
100954
|
-
})();
|
|
100955
|
-
}, [conversationId, apiBaseUrl, getAuthToken, updateMessageFeedback]);
|
|
100810
|
+
fetch("".concat(base, "/api/v1/support/feedback"), {
|
|
100811
|
+
method: "POST",
|
|
100812
|
+
headers: {
|
|
100813
|
+
"Content-Type": "application/json",
|
|
100814
|
+
"x-eshal-org": orgId
|
|
100815
|
+
},
|
|
100816
|
+
credentials: "include",
|
|
100817
|
+
body: JSON.stringify({
|
|
100818
|
+
messageContent,
|
|
100819
|
+
conversationId,
|
|
100820
|
+
organizationId: orgId,
|
|
100821
|
+
feedback,
|
|
100822
|
+
feedbackText
|
|
100823
|
+
})
|
|
100824
|
+
}).catch(err => console.error("Feedback submission error:", err));
|
|
100825
|
+
}, [orgId, conversationId, apiBaseUrl, updateMessageFeedback]);
|
|
100956
100826
|
|
|
100957
100827
|
// Refresh chat handler — reset conversation
|
|
100958
100828
|
const handleRefreshChat = reactExports.useCallback(() => {
|
|
@@ -100968,7 +100838,7 @@
|
|
|
100968
100838
|
const {
|
|
100969
100839
|
categories: filterCategories,
|
|
100970
100840
|
hasFilters: showFilterStep
|
|
100971
|
-
} = useFilterCatalog(apiBaseUrl,
|
|
100841
|
+
} = useFilterCatalog(apiBaseUrl, orgId);
|
|
100972
100842
|
|
|
100973
100843
|
// Copy chat handler — copy all messages to clipboard
|
|
100974
100844
|
const handleCopyChat = reactExports.useCallback(() => {
|
|
@@ -101073,7 +100943,7 @@
|
|
|
101073
100943
|
showPoweredBy: widgetConfig.showPoweredBy,
|
|
101074
100944
|
apiBaseUrl: apiBaseUrl,
|
|
101075
100945
|
apiKey: apiKey,
|
|
101076
|
-
|
|
100946
|
+
organizationId: widgetConfig.organizationId,
|
|
101077
100947
|
conversationId: conversationId,
|
|
101078
100948
|
conciergeId: (_agentConfig$concierg = agentConfig === null || agentConfig === void 0 ? void 0 : agentConfig.conciergeId) !== null && _agentConfig$concierg !== void 0 ? _agentConfig$concierg : agentConfig === null || agentConfig === void 0 ? void 0 : agentConfig.id,
|
|
101079
100949
|
onboardingQuestions: widgetConfig.onboardingQuestions,
|
|
@@ -101221,10 +101091,11 @@
|
|
|
101221
101091
|
}
|
|
101222
101092
|
|
|
101223
101093
|
// Render component
|
|
101224
|
-
//
|
|
101225
|
-
//
|
|
101226
|
-
const
|
|
101227
|
-
|
|
101094
|
+
// If orgId and apiBaseUrl are provided, use dynamic config
|
|
101095
|
+
// Otherwise, use the old static config approach for backward compatibility
|
|
101096
|
+
const widgetProps = config.orgId && config.apiBaseUrl ? _objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2({
|
|
101097
|
+
// Required props
|
|
101098
|
+
orgId: config.orgId,
|
|
101228
101099
|
apiBaseUrl: config.apiBaseUrl
|
|
101229
101100
|
}, options.wsBaseUrl !== undefined && {
|
|
101230
101101
|
wsBaseUrl: config.wsBaseUrl
|