@eshal-bot/chat-widget 0.1.67 → 0.1.69
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 +2 -2
- package/dist/chat-widget.js +155 -150
- package/dist/chat-widget.min.js +2 -2
- package/dist/chat-widget.umd.js +2 -2
- package/package.json +1 -1
package/dist/chat-widget.js
CHANGED
|
@@ -8010,20 +8010,21 @@
|
|
|
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)
|
|
8013
8014
|
*/
|
|
8014
|
-
const sendStreamingRequest = async (request, apiBaseUrl,
|
|
8015
|
+
const sendStreamingRequest = async (request, apiBaseUrl, getAuthToken) => {
|
|
8015
8016
|
const headers = {
|
|
8016
8017
|
'Content-Type': 'application/json',
|
|
8017
8018
|
Accept: 'text/event-stream'
|
|
8018
8019
|
};
|
|
8019
8020
|
|
|
8020
|
-
//
|
|
8021
|
-
// Mode B
|
|
8022
|
-
|
|
8023
|
-
|
|
8024
|
-
|
|
8025
|
-
|
|
8026
|
-
|
|
8021
|
+
// In-app JWT path: call getAuthToken() and set Authorization header
|
|
8022
|
+
// Mode B proxy: no auth header from widget; proxy injects x-eshal-sig + x-eshal-org server-side
|
|
8023
|
+
if (typeof getAuthToken === 'function') {
|
|
8024
|
+
const token = await getAuthToken();
|
|
8025
|
+
if (token) {
|
|
8026
|
+
headers['Authorization'] = "Bearer ".concat(token);
|
|
8027
|
+
}
|
|
8027
8028
|
}
|
|
8028
8029
|
const response = await fetch("".concat(apiBaseUrl.replace(/\/$/, ''), "/api/v1/support"), {
|
|
8029
8030
|
method: 'POST',
|
|
@@ -8792,15 +8793,12 @@
|
|
|
8792
8793
|
*/
|
|
8793
8794
|
const getWebSocketUrl = apiBaseUrl => {
|
|
8794
8795
|
if (!apiBaseUrl) {
|
|
8795
|
-
// No base URL yet (config still loading) — safe public default.
|
|
8796
8796
|
return 'wss://dev.eshal.ai/voice/ws';
|
|
8797
8797
|
}
|
|
8798
8798
|
try {
|
|
8799
8799
|
const url = new URL(apiBaseUrl);
|
|
8800
|
-
// http→ws, https→wss; point at the frontend voice-proxy path.
|
|
8801
8800
|
url.protocol = url.protocol.replace(/^http/, 'ws');
|
|
8802
8801
|
url.pathname = '/voice/ws';
|
|
8803
|
-
// Clean URL without a trailing slash.
|
|
8804
8802
|
return url.toString().replace(/\/$/, '');
|
|
8805
8803
|
} catch (error) {
|
|
8806
8804
|
console.error("Invalid API base URL provided for WebSocket:", error);
|
|
@@ -9146,7 +9144,7 @@
|
|
|
9146
9144
|
apiBaseUrl,
|
|
9147
9145
|
apiKey,
|
|
9148
9146
|
organizationId,
|
|
9149
|
-
|
|
9147
|
+
getAuthToken,
|
|
9150
9148
|
autoOpen,
|
|
9151
9149
|
openDelay,
|
|
9152
9150
|
darkMode,
|
|
@@ -9314,8 +9312,6 @@
|
|
|
9314
9312
|
const visitorProfileRef = reactExports.useRef(null);
|
|
9315
9313
|
const websocketUrl = reactExports.useMemo(() => {
|
|
9316
9314
|
if (!organizationId) return null;
|
|
9317
|
-
// Voice WS base is always derived from apiBaseUrl (the frontend origin
|
|
9318
|
-
// the widget is embedded with) → wss://{host}/voice/ws. No override.
|
|
9319
9315
|
const resolvedWsBaseUrl = getWebSocketUrl(apiBaseUrl);
|
|
9320
9316
|
return "".concat(resolvedWsBaseUrl, "/").concat(organizationId, "/").concat(bidiSessionId);
|
|
9321
9317
|
}, [apiBaseUrl, organizationId, bidiSessionId]);
|
|
@@ -9783,7 +9779,7 @@
|
|
|
9783
9779
|
if (apiType === 'userName' || apiType === 'email' || apiType === 'phone') {
|
|
9784
9780
|
// For onboarding fields, just consume the stream without processing.
|
|
9785
9781
|
// Single attempt — no retry, no user-visible partial state to clean up.
|
|
9786
|
-
const stream = await sendStreamingRequest(chatRequest, apiBaseUrl,
|
|
9782
|
+
const stream = await sendStreamingRequest(chatRequest, apiBaseUrl, getAuthToken);
|
|
9787
9783
|
const reader = stream.getReader();
|
|
9788
9784
|
const decoder = new TextDecoder();
|
|
9789
9785
|
while (true) {
|
|
@@ -9802,7 +9798,7 @@
|
|
|
9802
9798
|
let lastErr;
|
|
9803
9799
|
for (let attempt = 0; attempt <= STREAM_RETRY_DELAYS_MS.length; attempt += 1) {
|
|
9804
9800
|
try {
|
|
9805
|
-
const stream = await sendStreamingRequest(chatRequest, apiBaseUrl,
|
|
9801
|
+
const stream = await sendStreamingRequest(chatRequest, apiBaseUrl, getAuthToken);
|
|
9806
9802
|
await processStream(stream, setMessages);
|
|
9807
9803
|
updateActivity(organizationId);
|
|
9808
9804
|
return {
|
|
@@ -9830,7 +9826,7 @@
|
|
|
9830
9826
|
|
|
9831
9827
|
// Non-chat, non-onboarding apiType (shouldn't currently happen):
|
|
9832
9828
|
// fire and forget, single attempt.
|
|
9833
|
-
await sendStreamingRequest(chatRequest, apiBaseUrl,
|
|
9829
|
+
await sendStreamingRequest(chatRequest, apiBaseUrl, getAuthToken);
|
|
9834
9830
|
updateActivity(organizationId);
|
|
9835
9831
|
return {
|
|
9836
9832
|
success: true
|
|
@@ -9866,7 +9862,7 @@
|
|
|
9866
9862
|
// UX-M09: release the concurrency guard so the next send can proceed.
|
|
9867
9863
|
isSendingRef.current = false;
|
|
9868
9864
|
}
|
|
9869
|
-
}, [apiBaseUrl, apiKey, getNextMessageId,
|
|
9865
|
+
}, [apiBaseUrl, apiKey, getNextMessageId, getAuthToken, messages, bidiSessionId, routingChoice, widgetFilter, provideCitations]);
|
|
9870
9866
|
const handleOnboardingFormSubmit = reactExports.useCallback(async answers => {
|
|
9871
9867
|
const questions = getOnboardingQuestions(onboardingQuestions);
|
|
9872
9868
|
if (questions.length === 0) return;
|
|
@@ -94384,7 +94380,7 @@
|
|
|
94384
94380
|
let {
|
|
94385
94381
|
sources = [],
|
|
94386
94382
|
apiBaseUrl,
|
|
94387
|
-
|
|
94383
|
+
getAuthToken,
|
|
94388
94384
|
isDark,
|
|
94389
94385
|
textColor,
|
|
94390
94386
|
fontFamily,
|
|
@@ -94410,7 +94406,7 @@
|
|
|
94410
94406
|
// corpora that have none of the above.
|
|
94411
94407
|
const _downloadKey = s => s.url || s.source_id || s.source_name || "";
|
|
94412
94408
|
const handleFileDownload = async source => {
|
|
94413
|
-
if (!apiBaseUrl
|
|
94409
|
+
if (!apiBaseUrl) return;
|
|
94414
94410
|
// SharePoint proxy URLs are GET endpoints that stream the file
|
|
94415
94411
|
// directly via Microsoft Graph using the stored OAuth credential.
|
|
94416
94412
|
// Bypass the legacy POST /file-download lookup (which expects an
|
|
@@ -94436,11 +94432,14 @@
|
|
|
94436
94432
|
// set, or future deployments that set it) pass through
|
|
94437
94433
|
// unchanged.
|
|
94438
94434
|
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
|
+
}
|
|
94439
94440
|
res = await fetch(spUrl, {
|
|
94440
94441
|
method: "GET",
|
|
94441
|
-
headers:
|
|
94442
|
-
"x-eshal-org": organizationId
|
|
94443
|
-
},
|
|
94442
|
+
headers: spHeaders,
|
|
94444
94443
|
credentials: "include"
|
|
94445
94444
|
});
|
|
94446
94445
|
} else {
|
|
@@ -94456,15 +94455,18 @@
|
|
|
94456
94455
|
// - source.source_name (the user's original filename)
|
|
94457
94456
|
// Sending both lets the server skip the Milvus resolution
|
|
94458
94457
|
// 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
|
+
}
|
|
94459
94465
|
res = await fetch(url, {
|
|
94460
94466
|
method: "POST",
|
|
94461
|
-
headers:
|
|
94462
|
-
"Content-Type": "application/json",
|
|
94463
|
-
"x-eshal-org": organizationId
|
|
94464
|
-
},
|
|
94467
|
+
headers: dlHeaders,
|
|
94465
94468
|
credentials: "include",
|
|
94466
94469
|
body: JSON.stringify({
|
|
94467
|
-
org_id: organizationId,
|
|
94468
94470
|
source_id: source.source_id,
|
|
94469
94471
|
file_name: source.url || "",
|
|
94470
94472
|
source_name: source.source_name || ""
|
|
@@ -94649,7 +94651,7 @@
|
|
|
94649
94651
|
conciergeName,
|
|
94650
94652
|
companyName,
|
|
94651
94653
|
apiBaseUrl,
|
|
94652
|
-
|
|
94654
|
+
getAuthToken,
|
|
94653
94655
|
onPromptSuggestionClick,
|
|
94654
94656
|
allowFeedback = true,
|
|
94655
94657
|
provideCitations = true,
|
|
@@ -94821,7 +94823,7 @@
|
|
|
94821
94823
|
}), /*#__PURE__*/jsxRuntimeExports.jsx(MessageSources, {
|
|
94822
94824
|
sources: message.sources,
|
|
94823
94825
|
apiBaseUrl: apiBaseUrl,
|
|
94824
|
-
|
|
94826
|
+
getAuthToken: getAuthToken,
|
|
94825
94827
|
isDark: isDark,
|
|
94826
94828
|
textColor: messageTextColor,
|
|
94827
94829
|
fontFamily: messageFontFamily,
|
|
@@ -94847,7 +94849,7 @@
|
|
|
94847
94849
|
}), /*#__PURE__*/jsxRuntimeExports.jsx(MessageSources, {
|
|
94848
94850
|
sources: message.sources,
|
|
94849
94851
|
apiBaseUrl: apiBaseUrl,
|
|
94850
|
-
|
|
94852
|
+
getAuthToken: getAuthToken,
|
|
94851
94853
|
isDark: isDark,
|
|
94852
94854
|
textColor: messageTextColor,
|
|
94853
94855
|
fontFamily: messageFontFamily,
|
|
@@ -95027,7 +95029,7 @@
|
|
|
95027
95029
|
conciergeName,
|
|
95028
95030
|
companyName,
|
|
95029
95031
|
apiBaseUrl,
|
|
95030
|
-
|
|
95032
|
+
getAuthToken,
|
|
95031
95033
|
onPromptSuggestionClick,
|
|
95032
95034
|
allowFeedback,
|
|
95033
95035
|
provideCitations,
|
|
@@ -95071,7 +95073,7 @@
|
|
|
95071
95073
|
conciergeName: conciergeName,
|
|
95072
95074
|
companyName: companyName,
|
|
95073
95075
|
apiBaseUrl: apiBaseUrl,
|
|
95074
|
-
|
|
95076
|
+
getAuthToken: getAuthToken,
|
|
95075
95077
|
onPromptSuggestionClick: onPromptSuggestionClick,
|
|
95076
95078
|
allowFeedback: allowFeedback,
|
|
95077
95079
|
provideCitations: provideCitations,
|
|
@@ -95916,7 +95918,7 @@
|
|
|
95916
95918
|
conciergeName,
|
|
95917
95919
|
companyName,
|
|
95918
95920
|
apiBaseUrl,
|
|
95919
|
-
|
|
95921
|
+
getAuthToken,
|
|
95920
95922
|
onPromptSuggestionClick,
|
|
95921
95923
|
conversationResetKey,
|
|
95922
95924
|
allowFeedback,
|
|
@@ -96098,7 +96100,7 @@
|
|
|
96098
96100
|
conciergeName: conciergeName,
|
|
96099
96101
|
companyName: companyName,
|
|
96100
96102
|
apiBaseUrl: apiBaseUrl,
|
|
96101
|
-
|
|
96103
|
+
getAuthToken: getAuthToken,
|
|
96102
96104
|
allowFeedback: allowFeedback,
|
|
96103
96105
|
provideCitations: provideCitations,
|
|
96104
96106
|
onFeedback: onFeedback,
|
|
@@ -96130,7 +96132,7 @@
|
|
|
96130
96132
|
conciergeName: conciergeName,
|
|
96131
96133
|
companyName: companyName,
|
|
96132
96134
|
apiBaseUrl: apiBaseUrl,
|
|
96133
|
-
|
|
96135
|
+
getAuthToken: getAuthToken,
|
|
96134
96136
|
headerTextColor: headerTextColor
|
|
96135
96137
|
}, "skeleton-waiting");
|
|
96136
96138
|
}
|
|
@@ -99645,8 +99647,7 @@
|
|
|
99645
99647
|
showPoweredBy = true,
|
|
99646
99648
|
apiBaseUrl,
|
|
99647
99649
|
apiKey,
|
|
99648
|
-
|
|
99649
|
-
secretKey,
|
|
99650
|
+
getAuthToken,
|
|
99650
99651
|
conversationId,
|
|
99651
99652
|
conciergeId,
|
|
99652
99653
|
onboardingQuestions = [],
|
|
@@ -99794,7 +99795,7 @@
|
|
|
99794
99795
|
setCsatSubmitted(submitted);
|
|
99795
99796
|
if (submitted) setShowCsat(false);
|
|
99796
99797
|
} catch (_unused4) {}
|
|
99797
|
-
}, [conversationId
|
|
99798
|
+
}, [conversationId]);
|
|
99798
99799
|
|
|
99799
99800
|
// Auto-scroll hook
|
|
99800
99801
|
useAutoScroll(messagesContainerRef, [messages, isLoading], {
|
|
@@ -99932,14 +99933,15 @@
|
|
|
99932
99933
|
const handleCsatRating = async (rating, format) => {
|
|
99933
99934
|
setCsatSubmitted(true); // optimistic UI
|
|
99934
99935
|
setShowCsat(false);
|
|
99935
|
-
markCsatSubmitted(
|
|
99936
|
+
markCsatSubmitted(undefined); // persist so page refresh doesn't re-show CSAT
|
|
99936
99937
|
|
|
99937
99938
|
try {
|
|
99938
99939
|
const headers = {
|
|
99939
99940
|
"Content-Type": "application/json"
|
|
99940
99941
|
};
|
|
99941
|
-
if (
|
|
99942
|
-
|
|
99942
|
+
if (typeof getAuthToken === 'function') {
|
|
99943
|
+
const token = await getAuthToken();
|
|
99944
|
+
if (token) headers['Authorization'] = "Bearer ".concat(token);
|
|
99943
99945
|
}
|
|
99944
99946
|
const response = await fetch("".concat(apiBaseUrl.replace(/\/$/, ""), "/api/v1/support/csat"), {
|
|
99945
99947
|
method: "POST",
|
|
@@ -99949,7 +99951,6 @@
|
|
|
99949
99951
|
conversationId,
|
|
99950
99952
|
rating,
|
|
99951
99953
|
ratingFormat: format,
|
|
99952
|
-
organizationId,
|
|
99953
99954
|
conciergeId
|
|
99954
99955
|
})
|
|
99955
99956
|
});
|
|
@@ -99973,8 +99974,9 @@
|
|
|
99973
99974
|
const headers = {
|
|
99974
99975
|
"Content-Type": "application/json"
|
|
99975
99976
|
};
|
|
99976
|
-
if (
|
|
99977
|
-
|
|
99977
|
+
if (typeof getAuthToken === 'function') {
|
|
99978
|
+
const token = await getAuthToken();
|
|
99979
|
+
if (token) headers['Authorization'] = "Bearer ".concat(token);
|
|
99978
99980
|
}
|
|
99979
99981
|
const ratingFormat = normalizeCsatFormat(csatFormat);
|
|
99980
99982
|
const response = await fetch("".concat(apiBaseUrl.replace(/\/$/, ""), "/api/v1/support/csat"), {
|
|
@@ -99985,7 +99987,6 @@
|
|
|
99985
99987
|
conversationId,
|
|
99986
99988
|
rating: String(resolveRating),
|
|
99987
99989
|
ratingFormat,
|
|
99988
|
-
organizationId,
|
|
99989
99990
|
conciergeId,
|
|
99990
99991
|
feedback: resolveFeedbackText || undefined
|
|
99991
99992
|
})
|
|
@@ -100019,7 +100020,7 @@
|
|
|
100019
100020
|
} finally {
|
|
100020
100021
|
setIsSubmittingResolve(false);
|
|
100021
100022
|
}
|
|
100022
|
-
}, [isCsatEnabled, isSubmittingResolve, isConversationResolved, resolveRating, resolveFeedbackText, conversationId,
|
|
100023
|
+
}, [isCsatEnabled, isSubmittingResolve, isConversationResolved, resolveRating, resolveFeedbackText, conversationId, getAuthToken, conciergeId, apiBaseUrl, csatFormat, onResetConversation, onRefreshChat, onWidgetFilterChange]);
|
|
100023
100024
|
|
|
100024
100025
|
// Handle X button: first click shows form, second click closes
|
|
100025
100026
|
const handleCloseClick = reactExports.useCallback(() => {
|
|
@@ -100357,7 +100358,7 @@
|
|
|
100357
100358
|
conciergeName: conciergeName,
|
|
100358
100359
|
companyName: companyName,
|
|
100359
100360
|
apiBaseUrl: apiBaseUrl,
|
|
100360
|
-
|
|
100361
|
+
getAuthToken: getAuthToken,
|
|
100361
100362
|
conversationResetKey: conversationId,
|
|
100362
100363
|
allowFeedback: allowFeedback,
|
|
100363
100364
|
provideCitations: provideCitations,
|
|
@@ -100442,8 +100443,7 @@
|
|
|
100442
100443
|
|
|
100443
100444
|
/**
|
|
100444
100445
|
* @param {string|undefined|null} apiBaseUrl
|
|
100445
|
-
* @param {
|
|
100446
|
-
* @param {string|undefined|null} secretKey — Mode A/B: orgId resolved server-side
|
|
100446
|
+
* @param {Function|undefined|null} getAuthToken - async fn returning a Bearer token (in-app JWT path), or undefined/null (Mode B proxy)
|
|
100447
100447
|
* @returns {{
|
|
100448
100448
|
* categories: Array<{id:string,label:string,count:number,tags:string[]}>,
|
|
100449
100449
|
* loading: boolean,
|
|
@@ -100451,17 +100451,15 @@
|
|
|
100451
100451
|
* hasFilters: boolean,
|
|
100452
100452
|
* }}
|
|
100453
100453
|
*/
|
|
100454
|
-
function useFilterCatalog(apiBaseUrl,
|
|
100455
|
-
|
|
100456
|
-
// Mode C: orgId present (legacy) → orgId-in-path endpoint
|
|
100457
|
-
const hasAuth = !!(apiBaseUrl && (orgId || secretKey));
|
|
100454
|
+
function useFilterCatalog(apiBaseUrl, getAuthToken) {
|
|
100455
|
+
const hasAuth = !!apiBaseUrl;
|
|
100458
100456
|
const [state, setState] = reactExports.useState({
|
|
100459
100457
|
categories: [],
|
|
100460
100458
|
loading: hasAuth,
|
|
100461
100459
|
error: null
|
|
100462
100460
|
});
|
|
100463
100461
|
reactExports.useEffect(() => {
|
|
100464
|
-
if (!apiBaseUrl
|
|
100462
|
+
if (!apiBaseUrl) {
|
|
100465
100463
|
setState({
|
|
100466
100464
|
categories: [],
|
|
100467
100465
|
loading: false,
|
|
@@ -100469,7 +100467,7 @@
|
|
|
100469
100467
|
});
|
|
100470
100468
|
return;
|
|
100471
100469
|
}
|
|
100472
|
-
const cacheKey =
|
|
100470
|
+
const cacheKey = getAuthToken ? "".concat(apiBaseUrl, "|__session__") : "".concat(apiBaseUrl, "|__proxy__");
|
|
100473
100471
|
const cached = _PROCESS_CACHE.get(cacheKey);
|
|
100474
100472
|
if (cached) {
|
|
100475
100473
|
setState({
|
|
@@ -100485,45 +100483,54 @@
|
|
|
100485
100483
|
error: null
|
|
100486
100484
|
}));
|
|
100487
100485
|
const base = apiBaseUrl.replace(/\/$/, "");
|
|
100488
|
-
const url =
|
|
100489
|
-
|
|
100490
|
-
|
|
100491
|
-
|
|
100492
|
-
|
|
100493
|
-
|
|
100494
|
-
|
|
100495
|
-
|
|
100496
|
-
|
|
100497
|
-
|
|
100498
|
-
|
|
100486
|
+
const url = "".concat(base, "/api/v1/widget/filters");
|
|
100487
|
+
(async () => {
|
|
100488
|
+
try {
|
|
100489
|
+
const headers = {
|
|
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
|
+
});
|
|
100499
100528
|
}
|
|
100500
|
-
|
|
100501
|
-
return Array.isArray(body === null || body === void 0 ? void 0 : body.categories) ? body.categories : [];
|
|
100502
|
-
}).then(categories => {
|
|
100503
|
-
if (cancelled) return;
|
|
100504
|
-
_PROCESS_CACHE.set(cacheKey, categories);
|
|
100505
|
-
setState({
|
|
100506
|
-
categories,
|
|
100507
|
-
loading: false,
|
|
100508
|
-
error: null
|
|
100509
|
-
});
|
|
100510
|
-
}).catch(err => {
|
|
100511
|
-
if (cancelled) return;
|
|
100512
|
-
// Fail-soft: log and present empty so the UI hides itself.
|
|
100513
|
-
// We do NOT bubble the error to the caller because chat
|
|
100514
|
-
// should keep working even if the catalog is unreachable.
|
|
100515
|
-
// eslint-disable-next-line no-console
|
|
100516
|
-
console.warn("[useFilterCatalog] catalog fetch failed:", err);
|
|
100517
|
-
setState({
|
|
100518
|
-
categories: [],
|
|
100519
|
-
loading: false,
|
|
100520
|
-
error: err
|
|
100521
|
-
});
|
|
100522
|
-
});
|
|
100529
|
+
})();
|
|
100523
100530
|
return () => {
|
|
100524
100531
|
cancelled = true;
|
|
100525
100532
|
};
|
|
100526
|
-
}, [apiBaseUrl,
|
|
100533
|
+
}, [apiBaseUrl, getAuthToken]);
|
|
100527
100534
|
const hasFilters = reactExports.useMemo(() => Array.isArray(state.categories) && state.categories.length > 0, [state.categories]);
|
|
100528
100535
|
return _objectSpread2(_objectSpread2({}, state), {}, {
|
|
100529
100536
|
hasFilters
|
|
@@ -100602,24 +100609,28 @@
|
|
|
100602
100609
|
/**
|
|
100603
100610
|
* Fetches agent configuration from the deploy-agent endpoint
|
|
100604
100611
|
* @param {string} apiBaseUrl - Base URL for the API
|
|
100605
|
-
* @param {
|
|
100612
|
+
* @param {Function|undefined} getAuthToken - async fn returning a Bearer token (in-app JWT path), or undefined (Mode B proxy)
|
|
100606
100613
|
* @returns {Promise<object>} Agent configuration object
|
|
100607
100614
|
*/
|
|
100608
|
-
const fetchAgentConfig = async (apiBaseUrl,
|
|
100615
|
+
const fetchAgentConfig = async (apiBaseUrl, getAuthToken) => {
|
|
100609
100616
|
if (!apiBaseUrl) {
|
|
100610
100617
|
throw new Error("apiBaseUrl is required");
|
|
100611
100618
|
}
|
|
100612
|
-
// Mode A: secretKey in header, no orgId in URL
|
|
100613
|
-
// Mode B (proxy): no secretKey, no orgId — proxy intercepts and signs the request
|
|
100614
|
-
// Mode C legacy: orgId in URL path
|
|
100615
|
-
|
|
100616
100619
|
try {
|
|
100617
100620
|
const base = apiBaseUrl.replace(/\/$/, '');
|
|
100618
|
-
const url =
|
|
100621
|
+
const url = "".concat(base, "/api/v1/deploy-agent");
|
|
100619
100622
|
const headers = {
|
|
100620
100623
|
"Content-Type": "application/json"
|
|
100621
100624
|
};
|
|
100622
|
-
|
|
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
|
+
}
|
|
100623
100634
|
const response = await fetch(url, {
|
|
100624
100635
|
method: "GET",
|
|
100625
100636
|
headers,
|
|
@@ -100639,11 +100650,10 @@
|
|
|
100639
100650
|
const ChatWidget = _ref => {
|
|
100640
100651
|
var _agentConfig$concierg;
|
|
100641
100652
|
let {
|
|
100642
|
-
|
|
100643
|
-
|
|
100644
|
-
|
|
100645
|
-
|
|
100646
|
-
// legacy — kept for backward compatibility
|
|
100653
|
+
/**
|
|
100654
|
+
* @param {Function|undefined} getAuthToken - async fn returning a Keycloak Bearer token (in-app JWT path)
|
|
100655
|
+
*/
|
|
100656
|
+
getAuthToken,
|
|
100647
100657
|
apiBaseUrl,
|
|
100648
100658
|
// Optional override props (will override fetched config if provided)
|
|
100649
100659
|
darkMode,
|
|
@@ -100699,13 +100709,9 @@
|
|
|
100699
100709
|
setIsLoadingConfig(false);
|
|
100700
100710
|
return;
|
|
100701
100711
|
}
|
|
100702
|
-
// Mode B proxy: neither secretKey nor orgId in browser — proxy handles auth
|
|
100703
|
-
|
|
100704
100712
|
try {
|
|
100705
100713
|
setIsLoadingConfig(true);
|
|
100706
|
-
|
|
100707
|
-
// header and omit the orgId path segment. Pass orgId for Mode C legacy.
|
|
100708
|
-
const config = await fetchAgentConfig(apiBaseUrl, orgId, secretKey);
|
|
100714
|
+
const config = await fetchAgentConfig(apiBaseUrl, getAuthToken);
|
|
100709
100715
|
setAgentConfig(config);
|
|
100710
100716
|
setConfigError(null);
|
|
100711
100717
|
} catch (error) {
|
|
@@ -100716,11 +100722,11 @@
|
|
|
100716
100722
|
}
|
|
100717
100723
|
};
|
|
100718
100724
|
loadAgentConfig();
|
|
100719
|
-
}, [
|
|
100725
|
+
}, [apiBaseUrl, getAuthToken]);
|
|
100720
100726
|
|
|
100721
100727
|
// Map API response to widget props (use props as overrides if provided)
|
|
100722
100728
|
const widgetConfig = reactExports.useMemo(() => {
|
|
100723
|
-
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;
|
|
100729
|
+
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, _agentConfig$organiza;
|
|
100724
100730
|
if (!agentConfig) {
|
|
100725
100731
|
return null;
|
|
100726
100732
|
}
|
|
@@ -100779,7 +100785,6 @@
|
|
|
100779
100785
|
fontFamily: (_ref1 = fontFamily !== null && fontFamily !== void 0 ? fontFamily : typography.agentMessageBubbleFontFamily) !== null && _ref1 !== void 0 ? _ref1 : "Inter",
|
|
100780
100786
|
fontSize: (_ref10 = fontSize !== null && fontSize !== void 0 ? fontSize : typography.agentMessageBubbleFontSize) !== null && _ref10 !== void 0 ? _ref10 : "14px",
|
|
100781
100787
|
defaultLanguage: locale,
|
|
100782
|
-
organizationId: orgId,
|
|
100783
100788
|
headerTextBold: (_ref11 = headerTextBold !== null && headerTextBold !== void 0 ? headerTextBold : typography.headerTextBold) !== null && _ref11 !== void 0 ? _ref11 : false,
|
|
100784
100789
|
headerTextItalic: (_ref12 = headerTextItalic !== null && headerTextItalic !== void 0 ? headerTextItalic : typography.headerTextItalic) !== null && _ref12 !== void 0 ? _ref12 : false,
|
|
100785
100790
|
headerTextColor: (_typography$headerTex = typography.headerTextColor) !== null && _typography$headerTex !== void 0 ? _typography$headerTex : "#FFFFFF",
|
|
@@ -100816,9 +100821,12 @@
|
|
|
100816
100821
|
// every message and tell the backend to skip citation generation.
|
|
100817
100822
|
// Defaults to true so agents that haven't set the field keep showing
|
|
100818
100823
|
// citations (matches the testing-lab / chatbot-preview default).
|
|
100819
|
-
provideCitations: (_behavior$provideCita = behavior.provideCitations) !== null && _behavior$provideCita !== void 0 ? _behavior$provideCita : true
|
|
100824
|
+
provideCitations: (_behavior$provideCita = behavior.provideCitations) !== null && _behavior$provideCita !== void 0 ? _behavior$provideCita : true,
|
|
100825
|
+
// organizationId from the backend response — used internally for
|
|
100826
|
+
// session isolation (localStorage keys) and WS paths. Not a user prop.
|
|
100827
|
+
organizationId: (_agentConfig$organiza = agentConfig.organizationId) !== null && _agentConfig$organiza !== void 0 ? _agentConfig$organiza : null
|
|
100820
100828
|
};
|
|
100821
|
-
}, [agentConfig, darkMode, primaryColor, position, companyName, conciergeName, companyLogo, welcomeMessage, quickQuestions, quickQuestionsEnabled, textColor, agentMessageBubbleColor, userMessageBoxColor, assistantTextColor, userTextColor, fontFamily, fontSize, defaultLanguage,
|
|
100829
|
+
}, [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
|
|
100822
100830
|
// CSAT dependencies
|
|
100823
100831
|
// (kept minimal because these come from agentConfig)
|
|
100824
100832
|
]);
|
|
@@ -100837,7 +100845,7 @@
|
|
|
100837
100845
|
quickQuestions: [],
|
|
100838
100846
|
apiBaseUrl: apiBaseUrl || "",
|
|
100839
100847
|
apiKey: apiKey || "",
|
|
100840
|
-
|
|
100848
|
+
getAuthToken,
|
|
100841
100849
|
autoOpen: false,
|
|
100842
100850
|
openDelay: 0,
|
|
100843
100851
|
darkMode: false,
|
|
@@ -100845,7 +100853,7 @@
|
|
|
100845
100853
|
onboardingQuestions: [],
|
|
100846
100854
|
onboardingEnabled: false,
|
|
100847
100855
|
collectionPrompt: undefined
|
|
100848
|
-
}), [apiBaseUrl, apiKey,
|
|
100856
|
+
}), [apiBaseUrl, apiKey, getAuthToken]);
|
|
100849
100857
|
|
|
100850
100858
|
// ALWAYS call hooks before any early returns (Rules of Hooks)
|
|
100851
100859
|
const {
|
|
@@ -100887,14 +100895,9 @@
|
|
|
100887
100895
|
welcomeMessage: widgetConfig.welcomeMessage,
|
|
100888
100896
|
quickQuestions: widgetConfig.quickQuestions,
|
|
100889
100897
|
apiBaseUrl,
|
|
100890
|
-
// Voice WS endpoint is derived entirely from apiBaseUrl inside
|
|
100891
|
-
// useChatState (→ wss://{host}/voice/ws, the frontend voice proxy).
|
|
100892
|
-
// Nothing is forwarded/overridable here, so a stale backend or embed
|
|
100893
|
-
// wsBaseUrl can't point voice at the old (now non-public)
|
|
100894
|
-
// knowledge-api LoadBalancer.
|
|
100895
100898
|
apiKey,
|
|
100896
100899
|
organizationId: widgetConfig.organizationId,
|
|
100897
|
-
|
|
100900
|
+
getAuthToken,
|
|
100898
100901
|
autoOpen,
|
|
100899
100902
|
openDelay,
|
|
100900
100903
|
darkMode: widgetConfig.darkMode,
|
|
@@ -100921,25 +100924,33 @@
|
|
|
100921
100924
|
// navigation (but not a page refresh — feedback is not written to
|
|
100922
100925
|
// localStorage so it resets on reload as intended).
|
|
100923
100926
|
updateMessageFeedback(messageContent, feedback);
|
|
100924
|
-
if (!
|
|
100927
|
+
if (!conversationId || !apiBaseUrl) return;
|
|
100925
100928
|
const base = apiBaseUrl.replace(/\/$/, "");
|
|
100926
|
-
|
|
100927
|
-
|
|
100928
|
-
|
|
100929
|
-
|
|
100930
|
-
|
|
100931
|
-
|
|
100932
|
-
|
|
100933
|
-
|
|
100934
|
-
|
|
100935
|
-
|
|
100936
|
-
|
|
100937
|
-
|
|
100938
|
-
|
|
100939
|
-
|
|
100940
|
-
|
|
100941
|
-
|
|
100942
|
-
|
|
100929
|
+
(async () => {
|
|
100930
|
+
try {
|
|
100931
|
+
const feedbackHeaders = {
|
|
100932
|
+
"Content-Type": "application/json"
|
|
100933
|
+
};
|
|
100934
|
+
if (typeof getAuthToken === 'function') {
|
|
100935
|
+
const token = await getAuthToken();
|
|
100936
|
+
if (token) feedbackHeaders['Authorization'] = "Bearer ".concat(token);
|
|
100937
|
+
}
|
|
100938
|
+
await fetch("".concat(base, "/api/v1/support/feedback"), {
|
|
100939
|
+
method: "POST",
|
|
100940
|
+
headers: feedbackHeaders,
|
|
100941
|
+
credentials: "include",
|
|
100942
|
+
body: JSON.stringify({
|
|
100943
|
+
messageContent,
|
|
100944
|
+
conversationId,
|
|
100945
|
+
feedback,
|
|
100946
|
+
feedbackText
|
|
100947
|
+
})
|
|
100948
|
+
});
|
|
100949
|
+
} catch (err) {
|
|
100950
|
+
console.error("Feedback submission error:", err);
|
|
100951
|
+
}
|
|
100952
|
+
})();
|
|
100953
|
+
}, [conversationId, apiBaseUrl, getAuthToken, updateMessageFeedback]);
|
|
100943
100954
|
|
|
100944
100955
|
// Refresh chat handler — reset conversation
|
|
100945
100956
|
const handleRefreshChat = reactExports.useCallback(() => {
|
|
@@ -100955,7 +100966,7 @@
|
|
|
100955
100966
|
const {
|
|
100956
100967
|
categories: filterCategories,
|
|
100957
100968
|
hasFilters: showFilterStep
|
|
100958
|
-
} = useFilterCatalog(apiBaseUrl,
|
|
100969
|
+
} = useFilterCatalog(apiBaseUrl, getAuthToken);
|
|
100959
100970
|
|
|
100960
100971
|
// Copy chat handler — copy all messages to clipboard
|
|
100961
100972
|
const handleCopyChat = reactExports.useCallback(() => {
|
|
@@ -101060,8 +101071,7 @@
|
|
|
101060
101071
|
showPoweredBy: widgetConfig.showPoweredBy,
|
|
101061
101072
|
apiBaseUrl: apiBaseUrl,
|
|
101062
101073
|
apiKey: apiKey,
|
|
101063
|
-
|
|
101064
|
-
secretKey: secretKey,
|
|
101074
|
+
getAuthToken: getAuthToken,
|
|
101065
101075
|
conversationId: conversationId,
|
|
101066
101076
|
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,
|
|
101067
101077
|
onboardingQuestions: widgetConfig.onboardingQuestions,
|
|
@@ -101209,15 +101219,10 @@
|
|
|
101209
101219
|
}
|
|
101210
101220
|
|
|
101211
101221
|
// Render component
|
|
101212
|
-
// Mode
|
|
101213
|
-
//
|
|
101214
|
-
// Both: apiBaseUrl required
|
|
101215
|
-
// Mode B proxy: apiBaseUrl alone is enough — no secretKey or orgId in browser
|
|
101222
|
+
// Mode B proxy: apiBaseUrl alone is enough — no key or orgId in browser
|
|
101223
|
+
// In-app JWT: getAuthToken fn passed via config
|
|
101216
101224
|
const hasNewAuth = config.apiBaseUrl;
|
|
101217
101225
|
const widgetProps = hasNewAuth ? _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({
|
|
101218
|
-
// Auth props — secretKey replaces orgId in Mode A/B
|
|
101219
|
-
secretKey: config.secretKey || null,
|
|
101220
|
-
orgId: config.orgId || null,
|
|
101221
101226
|
apiBaseUrl: config.apiBaseUrl
|
|
101222
101227
|
}, options.wsBaseUrl !== undefined && {
|
|
101223
101228
|
wsBaseUrl: config.wsBaseUrl
|