@iblai/iblai-js 2.18.0 → 2.18.1
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.
|
@@ -189484,7 +189484,7 @@ function RetrievedDocumentsButton({ sessionId = '', documents, className, label,
|
|
|
189484
189484
|
// Safe inside clickable transcript rows: the row must not react.
|
|
189485
189485
|
event.stopPropagation();
|
|
189486
189486
|
setIsOpen(true);
|
|
189487
|
-
}, "data-testid": "retrieved-documents-button", children: [jsx(FileText, { className: "h-4 w-4 text-blue-600" }), jsx("span", { children: label !== null && label !== void 0 ? label : t('retrievedDocumentsButton.title') })] }), jsx(Dialog, { open: isOpen, onOpenChange: setIsOpen, children: jsxs(DialogContent, { className: "flex max-h-[80vh] flex-col gap-0 p-0 sm:max-w-[425px] md:max-w-[600px]", children: [jsx(DialogHeader, { className: "border-b px-4 py-2", children: jsxs(DialogTitle, { className: "flex items-center gap-2 text-base font-medium text-[#646464]", children: [jsx(FileText, { className: "h-5 w-5 text-blue-600" }), t('retrievedDocumentsButton.title')] }) }), jsx("div", { className: "min-h-0 flex-1 overflow-y-auto", "data-testid": "retrieved-documents-scroll", children: jsx(DocumentSidebar, { isModal: true, sessionId: sessionId, documents: documents }) })] }) })] }));
|
|
189487
|
+
}, "data-testid": "retrieved-documents-button", children: [jsx(FileText, { className: "h-4 w-4 text-blue-600" }), jsx("span", { children: label !== null && label !== void 0 ? label : t('retrievedDocumentsButton.title') })] }), jsx(Dialog, { open: isOpen, onOpenChange: setIsOpen, children: jsxs(DialogContent, { className: "flex max-h-[80vh] flex-col gap-0 p-0 sm:max-w-[425px] md:max-w-[600px]", children: [jsx(DialogHeader, { className: "border-b border-gray-200 px-4 py-2", children: jsxs(DialogTitle, { className: "flex items-center gap-2 text-base font-medium text-[#646464]", children: [jsx(FileText, { className: "h-5 w-5 text-blue-600" }), t('retrievedDocumentsButton.title')] }) }), jsx("div", { className: "min-h-0 flex-1 overflow-y-auto", "data-testid": "retrieved-documents-scroll", children: jsx(DocumentSidebar, { isModal: true, sessionId: sessionId, documents: documents }) })] }) })] }));
|
|
189488
189488
|
}
|
|
189489
189489
|
|
|
189490
189490
|
var COLLAPSIBLE_NAME = "Collapsible";
|
|
@@ -232420,6 +232420,154 @@ const useFinancial = ({ tenantKey, mentorId, userId, }) => {
|
|
|
232420
232420
|
};
|
|
232421
232421
|
};
|
|
232422
232422
|
|
|
232423
|
+
/**
|
|
232424
|
+
* One place that decides how a user is shown.
|
|
232425
|
+
*
|
|
232426
|
+
* Backend payloads name their user in a dozen ways — `email` / `lti_email` /
|
|
232427
|
+
* `user_email`, `username` / `student` / `lti_username`, `name` /
|
|
232428
|
+
* `full_name` / `user_full_name`, or a nested `user` object — and most of
|
|
232429
|
+
* them can be empty, `null`, or a placeholder like "None". Every surface used
|
|
232430
|
+
* to pick through those on its own. `resolveUserIdentity()` takes whatever
|
|
232431
|
+
* the payload is and returns what to show, what to open a profile on, and
|
|
232432
|
+
* the avatar initial, so the rule lives here and can be tweaked once.
|
|
232433
|
+
*
|
|
232434
|
+
* Default rule: a real full name first (see {@link hasDistinctName}), else
|
|
232435
|
+
* email → username → the anonymous label.
|
|
232436
|
+
*/
|
|
232437
|
+
const DEFAULT_USER_IDENTITY_ORDER = ['email', 'name', 'username'];
|
|
232438
|
+
/** Order used when the payload carries a real name (see {@link hasDistinctName}). */
|
|
232439
|
+
const NAME_FIRST_ORDER = ['name', 'email', 'username'];
|
|
232440
|
+
/**
|
|
232441
|
+
* Lower-case letters and digits of any script, accents and punctuation dropped,
|
|
232442
|
+
* so "Conrad Mugabe" / "conrad.mugabe" and "José" / "jose" compare equal.
|
|
232443
|
+
*/
|
|
232444
|
+
const identityKey = (value) => (value !== null && value !== void 0 ? value : '')
|
|
232445
|
+
.normalize('NFKD')
|
|
232446
|
+
.replace(/[^\p{L}\p{N}]/gu, '')
|
|
232447
|
+
.toLowerCase();
|
|
232448
|
+
/**
|
|
232449
|
+
* Whether `name` is a real name rather than a restatement of the username.
|
|
232450
|
+
*
|
|
232451
|
+
* Many payloads fill the name field with the username ("conradmugabe" →
|
|
232452
|
+
* "Conrad Mugabe", "fayyaz_4_ibleducation_com"), which tells an admin nothing
|
|
232453
|
+
* the email does not. A name that matches none of the usernames on the record
|
|
232454
|
+
* is the most recognisable thing on it — including for LTI learners, whose
|
|
232455
|
+
* minted `<opaque-id>@edx-lti-1p3.com` address and username identify nobody.
|
|
232456
|
+
*/
|
|
232457
|
+
function hasDistinctName(name, ...usernames) {
|
|
232458
|
+
const key = identityKey(name);
|
|
232459
|
+
if (!key)
|
|
232460
|
+
return false;
|
|
232461
|
+
return usernames.every((username) => key !== identityKey(username));
|
|
232462
|
+
}
|
|
232463
|
+
const DEFAULT_ANONYMOUS_LABEL = 'Anonymous';
|
|
232464
|
+
/** Values backends emit instead of "nothing". Compared case-insensitively. */
|
|
232465
|
+
const PLACEHOLDERS = new Set([
|
|
232466
|
+
'',
|
|
232467
|
+
'none',
|
|
232468
|
+
'null',
|
|
232469
|
+
'undefined',
|
|
232470
|
+
'n/a',
|
|
232471
|
+
'na',
|
|
232472
|
+
'-',
|
|
232473
|
+
'anonymous',
|
|
232474
|
+
'anonymoususer',
|
|
232475
|
+
]);
|
|
232476
|
+
/**
|
|
232477
|
+
* `local@domain.tld` with no whitespace, checked with a single pass rather than
|
|
232478
|
+
* a backtracking pattern (CodeQL flags `[^@]+@[^@]+\.[^@]+` as polynomial on
|
|
232479
|
+
* adversarial input).
|
|
232480
|
+
*/
|
|
232481
|
+
const isEmailLike = (value) => {
|
|
232482
|
+
const text = value.trim();
|
|
232483
|
+
if (!text || /\s/.test(text))
|
|
232484
|
+
return false;
|
|
232485
|
+
const at = text.indexOf('@');
|
|
232486
|
+
if (at <= 0 || at !== text.lastIndexOf('@') || at === text.length - 1)
|
|
232487
|
+
return false;
|
|
232488
|
+
const domain = text.slice(at + 1);
|
|
232489
|
+
const dot = domain.indexOf('.');
|
|
232490
|
+
// A dot somewhere inside the domain, with something after the last one.
|
|
232491
|
+
return dot > 0 && !domain.endsWith('.');
|
|
232492
|
+
};
|
|
232493
|
+
/** Trimmed string, or undefined for blanks, non-strings and placeholders. */
|
|
232494
|
+
const clean = (value) => {
|
|
232495
|
+
if (typeof value !== 'string')
|
|
232496
|
+
return undefined;
|
|
232497
|
+
const trimmed = value.trim();
|
|
232498
|
+
return PLACEHOLDERS.has(trimmed.toLowerCase()) ? undefined : trimmed;
|
|
232499
|
+
};
|
|
232500
|
+
const firstOf = (...candidates) => {
|
|
232501
|
+
for (const candidate of candidates) {
|
|
232502
|
+
const value = clean(candidate);
|
|
232503
|
+
if (value)
|
|
232504
|
+
return value;
|
|
232505
|
+
}
|
|
232506
|
+
return undefined;
|
|
232507
|
+
};
|
|
232508
|
+
const nested = (source) => source.user && typeof source.user === 'object' ? source.user : undefined;
|
|
232509
|
+
/**
|
|
232510
|
+
* Resolve how a user should be displayed from any payload that names one.
|
|
232511
|
+
*
|
|
232512
|
+
* - `email`: `lti_email`, `email`, `user_email`, the nested `user`'s email —
|
|
232513
|
+
* or a username field that turns out to hold an email address.
|
|
232514
|
+
* - `username`: `lti_username`, `username`, `student`, `user_username`, the
|
|
232515
|
+
* nested `user`'s username, or a bare string/number `user` value.
|
|
232516
|
+
* - `name`: `user_full_name`, `full_name`, `name`, `first_name last_name`, or
|
|
232517
|
+
* the nested `user`'s name.
|
|
232518
|
+
* - `profileUsername`: the first *platform* username (never an LTI claim).
|
|
232519
|
+
*
|
|
232520
|
+
* A string source is treated as an email when it looks like one, else as a
|
|
232521
|
+
* username. Placeholder strings ("None", "-", "anonymous"…) count as absent.
|
|
232522
|
+
*
|
|
232523
|
+
* The default order flips to name → email → username when the record carries a
|
|
232524
|
+
* real name (see {@link hasDistinctName}): "Joseph Oti" is more use to an admin
|
|
232525
|
+
* than his address, while a name that merely repeats the username is not, so
|
|
232526
|
+
* those records keep showing the email. An explicit `order` is always obeyed.
|
|
232527
|
+
*/
|
|
232528
|
+
function resolveUserIdentity(source, options = {}) {
|
|
232529
|
+
var _a, _b;
|
|
232530
|
+
const anonymousLabel = options.anonymousLabel || DEFAULT_ANONYMOUS_LABEL;
|
|
232531
|
+
const order = ((_a = options.order) === null || _a === void 0 ? void 0 : _a.length) ? options.order : DEFAULT_USER_IDENTITY_ORDER;
|
|
232532
|
+
const record = source && typeof source === 'object' ? source : { user: source !== null && source !== void 0 ? source : null };
|
|
232533
|
+
const inner = nested(record);
|
|
232534
|
+
// A bare `user` value is an id or username; numeric ids are still shown.
|
|
232535
|
+
const bareUser = typeof record.user === 'number' && Number.isFinite(record.user)
|
|
232536
|
+
? String(record.user)
|
|
232537
|
+
: typeof record.user === 'string'
|
|
232538
|
+
? clean(record.user)
|
|
232539
|
+
: undefined;
|
|
232540
|
+
let email = firstOf(record.lti_email, record.email, record.user_email, inner === null || inner === void 0 ? void 0 : inner.lti_email, inner === null || inner === void 0 ? void 0 : inner.email, inner === null || inner === void 0 ? void 0 : inner.user_email);
|
|
232541
|
+
const platformUsername = firstOf(record.username, record.student, record.user_username, inner === null || inner === void 0 ? void 0 : inner.username, inner === null || inner === void 0 ? void 0 : inner.student, inner === null || inner === void 0 ? void 0 : inner.user_username, bareUser && !isEmailLike(bareUser) ? bareUser : undefined);
|
|
232542
|
+
let username = firstOf(record.lti_username, inner === null || inner === void 0 ? void 0 : inner.lti_username, platformUsername);
|
|
232543
|
+
const composed = [clean(record.first_name), clean(record.last_name)].filter(Boolean).join(' ');
|
|
232544
|
+
const name = firstOf(record.user_full_name, record.full_name, record.name, composed, inner === null || inner === void 0 ? void 0 : inner.user_full_name, inner === null || inner === void 0 ? void 0 : inner.full_name, inner === null || inner === void 0 ? void 0 : inner.name);
|
|
232545
|
+
// A bare string that looks like an email is an email, and so is a
|
|
232546
|
+
// "username" field that actually carries one.
|
|
232547
|
+
if (!email && bareUser && isEmailLike(bareUser))
|
|
232548
|
+
email = bareUser;
|
|
232549
|
+
if (!email && username && isEmailLike(username)) {
|
|
232550
|
+
email = username;
|
|
232551
|
+
username = undefined;
|
|
232552
|
+
}
|
|
232553
|
+
const facts = { email, username, name };
|
|
232554
|
+
// Every username on the record counts: the platform one a profile opens on
|
|
232555
|
+
// and the LTI claim that would be displayed.
|
|
232556
|
+
const labelOrder = !((_b = options.order) === null || _b === void 0 ? void 0 : _b.length) && hasDistinctName(name, platformUsername, username)
|
|
232557
|
+
? NAME_FIRST_ORDER
|
|
232558
|
+
: order;
|
|
232559
|
+
const label = labelOrder.map((field) => facts[field]).find(Boolean);
|
|
232560
|
+
return {
|
|
232561
|
+
label: label !== null && label !== void 0 ? label : anonymousLabel,
|
|
232562
|
+
email,
|
|
232563
|
+
username,
|
|
232564
|
+
name,
|
|
232565
|
+
profileUsername: platformUsername,
|
|
232566
|
+
initial: (label !== null && label !== void 0 ? label : anonymousLabel).charAt(0).toUpperCase() || 'A',
|
|
232567
|
+
isAnonymous: !label,
|
|
232568
|
+
};
|
|
232569
|
+
}
|
|
232570
|
+
|
|
232423
232571
|
const TONES = {
|
|
232424
232572
|
amber: {
|
|
232425
232573
|
container: 'border-amber-200 bg-amber-50 text-amber-800',
|
|
@@ -232777,6 +232925,10 @@ const costTick = (value) => {
|
|
|
232777
232925
|
function SpendTab({ tenantKey, currentSPA, mentorId: currentMentorId, userId, onOpenUserTraces, }) {
|
|
232778
232926
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
|
|
232779
232927
|
const t = useT();
|
|
232928
|
+
// The label a cost row shows for its user, by the shared identity rule.
|
|
232929
|
+
const identify = React__default.useCallback((row) => resolveUserIdentity(row, {
|
|
232930
|
+
anonymousLabel: t('analyticsAnalyticsFinancialStats.anonymous'),
|
|
232931
|
+
}), [t]);
|
|
232780
232932
|
const { weeklyCostData, isLoadingWeeklyCostData, isErrorWeeklyCostData, monthlyCostData, isLoadingMonthlyCostData, isErrorMonthlyCostData, totalCostData, isLoadingTotalCostData, isErrorTotalCostData, detailedCostPerDayData, isLoadingDetailedCostPerDayData, detailedCostPerDayFilters, setDetailedCostPerDayFilters, detailedCostPerProviderData, isLoadingDetailedCostPerProviderData, detailedCostPerProviderFilters, setDetailedCostPerProviderFilters, detailedCostPerUserData, isLoadingDetailedCostPerUserData, detailedCostPerUserFilters, setDetailedCostPerUserFilters, costPerUserPage, setCostPerUserPage, costPerUserLimit, providerColors, detailedLLMCostData, isLoadingDetailedLLMCostData, detailedLLMCostFilters, setDetailedLLMCostFilters, } = useFinancial({ tenantKey, mentorId: currentMentorId, userId });
|
|
232781
232933
|
// Pagination handlers
|
|
232782
232934
|
const handleFirstPage = () => {
|
|
@@ -232871,7 +233023,7 @@ function SpendTab({ tenantKey, currentSPA, mentorId: currentMentorId, userId, on
|
|
|
232871
233023
|
shown: (_p = (_o = detailedLLMCostData === null || detailedLLMCostData === void 0 ? void 0 : detailedLLMCostData.rows) === null || _o === void 0 ? void 0 : _o.length) !== null && _p !== void 0 ? _p : 0,
|
|
232872
233024
|
}) })) : (jsx(EmptyStats, {})) })] }), jsx("div", { className: "grid grid-cols-1 gap-6", children: jsx(ChartCardWrapper, { title: t('analyticsAnalyticsFinancialStats.costPerUserTitle'), source: "clickhouse", label: t('analyticsAnalyticsFinancialStats.costPerUserLabel'), filters: detailedCostPerUserFilters, setOutsideFilters: setDetailedCostPerUserFilters, overflowAuto: true, height: "450px", description: t('analyticsAnalyticsFinancialStats.costPerUserDescription'), children: jsxs("div", { className: "space-y-4", children: [jsx("div", { className: "rounded-md", style: { borderColor: 'oklch(.922 0 0)', borderWidth: '1px' }, children: jsxs(Table, { children: [jsx(TableHeader, { children: jsxs(TableRow, { className: "h-[48px]", style: { borderColor: 'oklch(.922 0 0)' }, children: [jsx(TableHead, { className: "w-[250px]", children: t('analyticsAnalyticsFinancialStats.tableHeaderUserEmail') }), jsx(TableHead, { className: "w-[120px]", children: t('analyticsAnalyticsFinancialStats.tableHeaderTotalCost') }), hasSessions && (jsx(TableHead, { children: t('analyticsAnalyticsFinancialStats.tableHeaderSessions') })), hasLastActive && (jsx(TableHead, { className: "text-right", children: t('analyticsAnalyticsFinancialStats.tableHeaderLastActive') })), onOpenUserTraces && jsx(TableHead, { className: "w-[90px]" })] }) }), jsx(TableBody, { children: (_q = detailedCostPerUserData === null || detailedCostPerUserData === void 0 ? void 0 : detailedCostPerUserData.rows) === null || _q === void 0 ? void 0 : _q.map((user, index) => {
|
|
232873
233025
|
var _a;
|
|
232874
|
-
return (jsxs(TableRow, { className: cn(index % 2 === 0 ? 'bg-gray-50' : '', 'h-[48px]'), style: { borderColor: 'oklch(.922 0 0)' }, children: [jsx(TableCell, { className: "font-medium", children: jsx("div", { className: "flex items-center", children:
|
|
233026
|
+
return (jsxs(TableRow, { className: cn(index % 2 === 0 ? 'bg-gray-50' : '', 'h-[48px]'), style: { borderColor: 'oklch(.922 0 0)' }, children: [jsx(TableCell, { className: "font-medium", children: jsx("div", { className: "flex items-center", children: jsx(UserProfileLink, { tenantKey: tenantKey, currentSPA: currentSPA, username: user.username, display: identify(user).label }) }) }), jsxs(TableCell, { className: "font-medium", children: ["$", Number(user.total_cost || 0).toFixed(2)] }), hasSessions && jsx(TableCell, { children: (_a = user.sessions) !== null && _a !== void 0 ? _a : '\u2014' }), hasLastActive && (jsx(TableCell, { className: "text-right", children: user.last_active ? formatRelativeTime$2(user.last_active) : '-' })), onOpenUserTraces && (jsx(TableCell, { className: "text-right", children: (user === null || user === void 0 ? void 0 : user.username) || (user === null || user === void 0 ? void 0 : user.user_email) || (user === null || user === void 0 ? void 0 : user.email) ? (jsx("button", { type: "button", className: "cursor-pointer text-sm font-semibold text-blue-600 hover:text-blue-700 hover:underline", onClick: () => onOpenUserTraces(String(user.username || user.user_email || user.email)), children: t('analyticsAnalyticsFinancialStats.viewTraces') })) : null }))] }, index));
|
|
232875
233027
|
}) })] }) }), isLoadingDetailedCostPerUserData && (jsx("div", { className: "h-[60px]", children: jsx(ChartLoading, {}) })), !isLoadingDetailedCostPerUserData &&
|
|
232876
233028
|
(!((_r = detailedCostPerUserData === null || detailedCostPerUserData === void 0 ? void 0 : detailedCostPerUserData.rows) === null || _r === void 0 ? void 0 : _r.length) ||
|
|
232877
233029
|
((_s = detailedCostPerUserData === null || detailedCostPerUserData === void 0 ? void 0 : detailedCostPerUserData.rows) === null || _s === void 0 ? void 0 : _s.length) === 0) && (jsx("div", { className: "h-[60px]", children: jsx(EmptyStats, {}) })), detailedCostPerUserData && detailedCostPerUserData.total_pages > 1 && (jsxs("div", { className: "flex items-center justify-between py-3", children: [jsx("div", { className: "text-sm text-gray-700", children: t('analyticsAnalyticsFinancialStats.paginationShowing', {
|
|
@@ -233362,7 +233514,7 @@ function SectionHeading({ icon, children }) {
|
|
|
233362
233514
|
return (jsxs("h4", { className: "mb-2 flex items-center gap-1.5 text-xs font-semibold tracking-wide text-gray-700 uppercase", children: [icon, children] }));
|
|
233363
233515
|
}
|
|
233364
233516
|
const MODEL_KEYS = new Set(['llm_model', 'llm_provider']);
|
|
233365
|
-
function MetadataSection({ metadata, renderModel, }) {
|
|
233517
|
+
function MetadataSection({ metadata, renderModel, tenantKey, mentorId, }) {
|
|
233366
233518
|
var _a, _b;
|
|
233367
233519
|
const t = useT();
|
|
233368
233520
|
const labelFor = useFieldLabels();
|
|
@@ -233370,7 +233522,9 @@ function MetadataSection({ metadata, renderModel, }) {
|
|
|
233370
233522
|
const model = ((_a = metadata.llm_model) !== null && _a !== void 0 ? _a : '').trim() || undefined;
|
|
233371
233523
|
const provider = ((_b = metadata.llm_provider) !== null && _b !== void 0 ? _b : '').trim() || undefined;
|
|
233372
233524
|
const hasModel = !!(model || provider);
|
|
233373
|
-
return (jsxs("section", { "data-testid": "transcript-turn-metadata", children: [jsx(SectionHeading, { icon: jsx(Info$3, { className: "h-3.5 w-3.5", "aria-hidden": "true" }), children: t('analyticsTranscriptTurnDetails.metadata') }), hasModel && (jsx("div", { "data-testid": "transcript-turn-model", className: "mb-2", children:
|
|
233525
|
+
return (jsxs("section", { "data-testid": "transcript-turn-metadata", children: [jsx(SectionHeading, { icon: jsx(Info$3, { className: "h-3.5 w-3.5", "aria-hidden": "true" }), children: t('analyticsTranscriptTurnDetails.metadata') }), hasModel && (jsx("div", { "data-testid": "transcript-turn-model", className: "mb-2", children: renderModel
|
|
233526
|
+
? renderModel({ model, provider })
|
|
233527
|
+
: defaultRenderModel({ model, provider, tenantKey, mentorId }) })), jsx(DetailRows, { entries: rest, labelFor: labelFor })] }));
|
|
233374
233528
|
}
|
|
233375
233529
|
function RequestContextSection({ requestContext }) {
|
|
233376
233530
|
const t = useT();
|
|
@@ -233385,7 +233539,7 @@ function RequestContextSection({ requestContext }) {
|
|
|
233385
233539
|
*/
|
|
233386
233540
|
const defaultRenderDocuments = (documents) => (jsx(RetrievedDocumentsButton, { documents: toRetrievedDocuments(documents) }));
|
|
233387
233541
|
const defaultRenderToolCalls = (toolCalls) => (jsx(ToolCallIndicator, { toolCalls: toToolCallInfos(toolCalls), showResults: true }));
|
|
233388
|
-
const defaultRenderModel = ({ model, provider }) =>
|
|
233542
|
+
const defaultRenderModel = ({ model, provider, tenantKey, mentorId, }) => jsx(LlmModelBadge, { model: model, provider: provider, tenantKey: tenantKey, mentorId: mentorId });
|
|
233389
233543
|
const defaultRenderFiles = (files) => (jsx(TranscriptFileCards, { files: files }));
|
|
233390
233544
|
/**
|
|
233391
233545
|
* Collapsible "Sources / Tools / Metadata" panel for one AI turn of an admin
|
|
@@ -233395,7 +233549,7 @@ const defaultRenderFiles = (files) => (jsx(TranscriptFileCards, { files: files }
|
|
|
233395
233549
|
* renders nothing at all when every field is null or empty, so it is safe to
|
|
233396
233550
|
* drop under every AI bubble.
|
|
233397
233551
|
*/
|
|
233398
|
-
function TranscriptTurnDetails({ turn, className, defaultOpen = false, renderDocuments, renderToolCalls, renderModel, renderFiles, }) {
|
|
233552
|
+
function TranscriptTurnDetails({ turn, className, defaultOpen = false, renderDocuments, renderToolCalls, renderModel, renderFiles, tenantKey, mentorId, }) {
|
|
233399
233553
|
const t = useT();
|
|
233400
233554
|
const labelFor = useFieldLabels();
|
|
233401
233555
|
const [open, setOpen] = React__default.useState(defaultOpen);
|
|
@@ -233409,7 +233563,7 @@ function TranscriptTurnDetails({ turn, className, defaultOpen = false, renderDoc
|
|
|
233409
233563
|
const requestContext = hasEntries(turn === null || turn === void 0 ? void 0 : turn.request_context) ? turn === null || turn === void 0 ? void 0 : turn.request_context : null;
|
|
233410
233564
|
return (jsx(DetailCtx.Provider, { value: detailContext, children: jsxs(Collapsible, { open: open, onOpenChange: setOpen, className: cn('mt-2', className), "data-testid": "transcript-turn-details", children: [jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [jsx(CollapsibleTrigger, { asChild: true, children: jsxs(Button$1, { type: "button", variant: "outline", size: "sm", className: "flex items-center gap-2 border-gray-200 bg-white text-gray-700 hover:bg-gray-50", "aria-expanded": open, children: [jsx(ChevronDown, { className: cn('h-4 w-4 transition-transform', open && 'rotate-180'), "aria-hidden": "true" }), open
|
|
233411
233565
|
? t('analyticsTranscriptTurnDetails.hideDetails')
|
|
233412
|
-
: t('analyticsTranscriptTurnDetails.showDetails')] }) }), documents.length > 0 && (jsx("div", { "data-testid": "transcript-turn-sources", className: "contents", children: (renderDocuments !== null && renderDocuments !== void 0 ? renderDocuments : defaultRenderDocuments)(documents) })), jsx(TranscriptRollupBadges, { documentsCount: 0, toolCallsCount: toolCalls.length })] }), jsxs(CollapsibleContent, { className: "mt-2 space-y-5 rounded-lg border border-gray-200 bg-white p-3", children: [toolCalls.length > 0 && (jsx("div", { "data-testid": "transcript-turn-tools", children: (renderToolCalls !== null && renderToolCalls !== void 0 ? renderToolCalls : defaultRenderToolCalls)(toolCalls) })), metadata && jsx(MetadataSection, { metadata: metadata, renderModel: renderModel }), requestContext && jsx(RequestContextSection, { requestContext: requestContext })] })] }) }));
|
|
233566
|
+
: t('analyticsTranscriptTurnDetails.showDetails')] }) }), documents.length > 0 && (jsx("div", { "data-testid": "transcript-turn-sources", className: "contents", children: (renderDocuments !== null && renderDocuments !== void 0 ? renderDocuments : defaultRenderDocuments)(documents) })), jsx(TranscriptRollupBadges, { documentsCount: 0, toolCallsCount: toolCalls.length })] }), jsxs(CollapsibleContent, { className: "mt-2 space-y-5 rounded-lg border border-gray-200 bg-white p-3", children: [toolCalls.length > 0 && (jsx("div", { "data-testid": "transcript-turn-tools", children: (renderToolCalls !== null && renderToolCalls !== void 0 ? renderToolCalls : defaultRenderToolCalls)(toolCalls) })), metadata && (jsx(MetadataSection, { metadata: metadata, renderModel: renderModel, tenantKey: tenantKey, mentorId: mentorId })), requestContext && jsx(RequestContextSection, { requestContext: requestContext })] })] }) }));
|
|
233413
233567
|
}
|
|
233414
233568
|
/**
|
|
233415
233569
|
* "Documents · N" / "Tools · N" chips for a conversation row, from the
|
|
@@ -233425,118 +233579,6 @@ function TranscriptRollupBadges({ documentsCount, toolCallsCount, className, })
|
|
|
233425
233579
|
return (jsxs("span", { className: cn('inline-flex flex-wrap items-center gap-1.5', className), "data-testid": "transcript-rollup-badges", children: [documents > 0 && (jsxs(Badge, { variant: "outline", className: "gap-1 border-transparent bg-blue-50 font-normal text-blue-700", "data-testid": "transcript-sources-badge", children: [jsx(FileText, { className: "h-3 w-3", "aria-hidden": "true" }), t('analyticsTranscriptTurnDetails.sourcesBadge', { count: documents })] })), tools > 0 && (jsxs(Badge, { variant: "outline", className: "gap-1 border-transparent bg-amber-50 font-normal text-amber-800", "data-testid": "transcript-tools-badge", children: [jsx(Wrench, { className: "h-3 w-3", "aria-hidden": "true" }), t('analyticsTranscriptTurnDetails.toolsBadge', { count: tools })] }))] }));
|
|
233426
233580
|
}
|
|
233427
233581
|
|
|
233428
|
-
/**
|
|
233429
|
-
* One place that decides how a user is shown.
|
|
233430
|
-
*
|
|
233431
|
-
* Backend payloads name their user in a dozen ways — `email` / `lti_email` /
|
|
233432
|
-
* `user_email`, `username` / `student` / `lti_username`, `name` /
|
|
233433
|
-
* `full_name` / `user_full_name`, or a nested `user` object — and most of
|
|
233434
|
-
* them can be empty, `null`, or a placeholder like "None". Every surface used
|
|
233435
|
-
* to pick through those on its own. `resolveUserIdentity()` takes whatever
|
|
233436
|
-
* the payload is and returns what to show, what to open a profile on, and
|
|
233437
|
-
* the avatar initial, so the rule lives here and can be tweaked once.
|
|
233438
|
-
*
|
|
233439
|
-
* Default rule: email → full name → username → the anonymous label.
|
|
233440
|
-
*/
|
|
233441
|
-
const DEFAULT_USER_IDENTITY_ORDER = ['email', 'name', 'username'];
|
|
233442
|
-
const DEFAULT_ANONYMOUS_LABEL = 'Anonymous';
|
|
233443
|
-
/** Values backends emit instead of "nothing". Compared case-insensitively. */
|
|
233444
|
-
const PLACEHOLDERS = new Set([
|
|
233445
|
-
'',
|
|
233446
|
-
'none',
|
|
233447
|
-
'null',
|
|
233448
|
-
'undefined',
|
|
233449
|
-
'n/a',
|
|
233450
|
-
'na',
|
|
233451
|
-
'-',
|
|
233452
|
-
'anonymous',
|
|
233453
|
-
'anonymoususer',
|
|
233454
|
-
]);
|
|
233455
|
-
/**
|
|
233456
|
-
* `local@domain.tld` with no whitespace, checked with a single pass rather than
|
|
233457
|
-
* a backtracking pattern (CodeQL flags `[^@]+@[^@]+\.[^@]+` as polynomial on
|
|
233458
|
-
* adversarial input).
|
|
233459
|
-
*/
|
|
233460
|
-
const isEmailLike = (value) => {
|
|
233461
|
-
const text = value.trim();
|
|
233462
|
-
if (!text || /\s/.test(text))
|
|
233463
|
-
return false;
|
|
233464
|
-
const at = text.indexOf('@');
|
|
233465
|
-
if (at <= 0 || at !== text.lastIndexOf('@') || at === text.length - 1)
|
|
233466
|
-
return false;
|
|
233467
|
-
const domain = text.slice(at + 1);
|
|
233468
|
-
const dot = domain.indexOf('.');
|
|
233469
|
-
// A dot somewhere inside the domain, with something after the last one.
|
|
233470
|
-
return dot > 0 && !domain.endsWith('.');
|
|
233471
|
-
};
|
|
233472
|
-
/** Trimmed string, or undefined for blanks, non-strings and placeholders. */
|
|
233473
|
-
const clean = (value) => {
|
|
233474
|
-
if (typeof value !== 'string')
|
|
233475
|
-
return undefined;
|
|
233476
|
-
const trimmed = value.trim();
|
|
233477
|
-
return PLACEHOLDERS.has(trimmed.toLowerCase()) ? undefined : trimmed;
|
|
233478
|
-
};
|
|
233479
|
-
const firstOf = (...candidates) => {
|
|
233480
|
-
for (const candidate of candidates) {
|
|
233481
|
-
const value = clean(candidate);
|
|
233482
|
-
if (value)
|
|
233483
|
-
return value;
|
|
233484
|
-
}
|
|
233485
|
-
return undefined;
|
|
233486
|
-
};
|
|
233487
|
-
const nested = (source) => source.user && typeof source.user === 'object' ? source.user : undefined;
|
|
233488
|
-
/**
|
|
233489
|
-
* Resolve how a user should be displayed from any payload that names one.
|
|
233490
|
-
*
|
|
233491
|
-
* - `email`: `lti_email`, `email`, `user_email`, the nested `user`'s email —
|
|
233492
|
-
* or a username field that turns out to hold an email address.
|
|
233493
|
-
* - `username`: `lti_username`, `username`, `student`, `user_username`, the
|
|
233494
|
-
* nested `user`'s username, or a bare string/number `user` value.
|
|
233495
|
-
* - `name`: `user_full_name`, `full_name`, `name`, `first_name last_name`, or
|
|
233496
|
-
* the nested `user`'s name.
|
|
233497
|
-
* - `profileUsername`: the first *platform* username (never an LTI claim).
|
|
233498
|
-
*
|
|
233499
|
-
* A string source is treated as an email when it looks like one, else as a
|
|
233500
|
-
* username. Placeholder strings ("None", "-", "anonymous"…) count as absent.
|
|
233501
|
-
*/
|
|
233502
|
-
function resolveUserIdentity(source, options = {}) {
|
|
233503
|
-
var _a;
|
|
233504
|
-
const anonymousLabel = options.anonymousLabel || DEFAULT_ANONYMOUS_LABEL;
|
|
233505
|
-
const order = ((_a = options.order) === null || _a === void 0 ? void 0 : _a.length) ? options.order : DEFAULT_USER_IDENTITY_ORDER;
|
|
233506
|
-
const record = source && typeof source === 'object' ? source : { user: source !== null && source !== void 0 ? source : null };
|
|
233507
|
-
const inner = nested(record);
|
|
233508
|
-
// A bare `user` value is an id or username; numeric ids are still shown.
|
|
233509
|
-
const bareUser = typeof record.user === 'number' && Number.isFinite(record.user)
|
|
233510
|
-
? String(record.user)
|
|
233511
|
-
: typeof record.user === 'string'
|
|
233512
|
-
? clean(record.user)
|
|
233513
|
-
: undefined;
|
|
233514
|
-
let email = firstOf(record.lti_email, record.email, record.user_email, inner === null || inner === void 0 ? void 0 : inner.lti_email, inner === null || inner === void 0 ? void 0 : inner.email, inner === null || inner === void 0 ? void 0 : inner.user_email);
|
|
233515
|
-
const platformUsername = firstOf(record.username, record.student, record.user_username, inner === null || inner === void 0 ? void 0 : inner.username, inner === null || inner === void 0 ? void 0 : inner.student, inner === null || inner === void 0 ? void 0 : inner.user_username, bareUser && !isEmailLike(bareUser) ? bareUser : undefined);
|
|
233516
|
-
let username = firstOf(record.lti_username, inner === null || inner === void 0 ? void 0 : inner.lti_username, platformUsername);
|
|
233517
|
-
const composed = [clean(record.first_name), clean(record.last_name)].filter(Boolean).join(' ');
|
|
233518
|
-
const name = firstOf(record.user_full_name, record.full_name, record.name, composed, inner === null || inner === void 0 ? void 0 : inner.user_full_name, inner === null || inner === void 0 ? void 0 : inner.full_name, inner === null || inner === void 0 ? void 0 : inner.name);
|
|
233519
|
-
// A bare string that looks like an email is an email, and so is a
|
|
233520
|
-
// "username" field that actually carries one.
|
|
233521
|
-
if (!email && bareUser && isEmailLike(bareUser))
|
|
233522
|
-
email = bareUser;
|
|
233523
|
-
if (!email && username && isEmailLike(username)) {
|
|
233524
|
-
email = username;
|
|
233525
|
-
username = undefined;
|
|
233526
|
-
}
|
|
233527
|
-
const facts = { email, username, name };
|
|
233528
|
-
const label = order.map((field) => facts[field]).find(Boolean);
|
|
233529
|
-
return {
|
|
233530
|
-
label: label !== null && label !== void 0 ? label : anonymousLabel,
|
|
233531
|
-
email,
|
|
233532
|
-
username,
|
|
233533
|
-
name,
|
|
233534
|
-
profileUsername: platformUsername,
|
|
233535
|
-
initial: (label !== null && label !== void 0 ? label : anonymousLabel).charAt(0).toUpperCase() || 'A',
|
|
233536
|
-
isAnonymous: !label,
|
|
233537
|
-
};
|
|
233538
|
-
}
|
|
233539
|
-
|
|
233540
233582
|
// Utility function to format relative time
|
|
233541
233583
|
const formatRelativeTime = (dateString, t) => {
|
|
233542
233584
|
const date = new Date(dateString);
|
|
@@ -233582,8 +233624,13 @@ const getMessageContent = (content) => {
|
|
|
233582
233624
|
* in the order the caller asks for; "-" when the row names nobody.
|
|
233583
233625
|
*/
|
|
233584
233626
|
const identifyUser = (source, order) => resolveUserIdentity(source, { order, anonymousLabel: '-' }).label;
|
|
233585
|
-
/**
|
|
233586
|
-
|
|
233627
|
+
/**
|
|
233628
|
+
* The model as the nav bar shows it (logo + display name); hosts may override
|
|
233629
|
+
* via `turnDetails.renderModel`. The catalogue is read for the tenant and the
|
|
233630
|
+
* agent the conversation ran on — route params only carry those on the mentor
|
|
233631
|
+
* SPA, so analytics passes them explicitly.
|
|
233632
|
+
*/
|
|
233633
|
+
const renderModelBadge = ({ model, provider, tenantKey, mentorId, }) => jsx(LlmModelBadge, { model: model, provider: provider, tenantKey: tenantKey, mentorId: mentorId });
|
|
233587
233634
|
/** Newer deployments name the provider on rows and summaries; older ones only the model. */
|
|
233588
233635
|
const providerOf = (source) => {
|
|
233589
233636
|
const value = source === null || source === void 0 ? void 0 : source.llm_provider;
|
|
@@ -233619,7 +233666,7 @@ function TranscriptDocumentsButton({ detail, className, }) {
|
|
|
233619
233666
|
return (jsx(RetrievedDocumentsButton, { sessionId: sessionId, documents: documents.length > 0 ? documents : undefined, className: className }));
|
|
233620
233667
|
}
|
|
233621
233668
|
function AnalyticsTranscriptsStats({ tenantKey, currentSPA, mentorId, selectedMentorId, usergroupIds, turnDetails, }) {
|
|
233622
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
|
|
233669
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
|
|
233623
233670
|
const t = useT();
|
|
233624
233671
|
const currentMentorId = useScopedMentorId(selectedMentorId, mentorId);
|
|
233625
233672
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
@@ -233627,6 +233674,10 @@ function AnalyticsTranscriptsStats({ tenantKey, currentSPA, mentorId, selectedMe
|
|
|
233627
233674
|
const router = useRouter();
|
|
233628
233675
|
const pathname = usePathname();
|
|
233629
233676
|
const { transcriptsMessagesData, isLoadingTranscriptsMessagesData, isFetchingTranscriptsMessagesData, filters, setFilters, handleClearFilters, currentPage, handlePageChange, selectedTranscript, setSelectedTranscript, transcriptsMessagesDetailsData, isLoadingTranscriptsMessagesDetailsData, isErrorTranscriptsMessagesDetailsData, transcriptsConversationHeadlineData, isLoadingTranscriptsConversationHeadlineData, } = useTranscripts({ tenantKey, mentorId: currentMentorId, usergroupIds });
|
|
233677
|
+
// The agent the open transcript ran on — the catalogue scope for its model
|
|
233678
|
+
// badge. Hosts that browse across agents (the LMS analytics route) pass no
|
|
233679
|
+
// mentorId, so the transcript's own agent is the only correct scope.
|
|
233680
|
+
const detailMentorId = ((_a = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _a === void 0 ? void 0 : _a.mentor_unique_id) || currentMentorId;
|
|
233630
233681
|
// Read session_id from URL on mount and select the transcript
|
|
233631
233682
|
useEffect(() => {
|
|
233632
233683
|
const sessionIdParam = searchParams === null || searchParams === void 0 ? void 0 : searchParams.get('session_id');
|
|
@@ -233739,15 +233790,17 @@ function AnalyticsTranscriptsStats({ tenantKey, currentSPA, mentorId, selectedMe
|
|
|
233739
233790
|
? ((_e = turnDetails === null || turnDetails === void 0 ? void 0 : turnDetails.renderModel) !== null && _e !== void 0 ? _e : renderModelBadge)({
|
|
233740
233791
|
model: transcriptsMessagesDetailsData.summary.model,
|
|
233741
233792
|
provider: (_f = providerOf(transcriptsMessagesDetailsData.summary)) !== null && _f !== void 0 ? _f : providerOfTurns(transcriptsMessagesDetailsData.messages),
|
|
233793
|
+
tenantKey,
|
|
233794
|
+
mentorId: detailMentorId,
|
|
233742
233795
|
})
|
|
233743
233796
|
: '-'] }), jsx(TranscriptDocumentsButton, { detail: transcriptsMessagesDetailsData, className: "mt-2" })] })] }), jsx("div", { className: "flex-1 overflow-y-auto space-y-4 min-h-0", children: ((_g = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.messages) === null || _g === void 0 ? void 0 : _g.length) > 0 ? ((_h = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.messages) === null || _h === void 0 ? void 0 : _h.map((message, index) => {
|
|
233744
233797
|
var _a, _b;
|
|
233745
|
-
return (jsxs(React__default.Fragment, { children: [((message === null || message === void 0 ? void 0 : message.human) || !!((_a = message === null || message === void 0 ? void 0 : message.human_files) === null || _a === void 0 ? void 0 : _a.length)) && (jsxs("div", { className: `p-3 rounded-lg bg-blue-100 ml-4`, children: [jsx("div", { className: "flex justify-between items-start mb-1", children: jsx("span", { className: "font-medium text-sm", children: t('analyticsAnalyticsTranscriptsStats.roleUser') }) }), message.human && (jsx("p", { className: "text-sm", children: getMessageContent(message.human) })), jsx(TranscriptFiles, { files: message.human_files })] }, `${index}-human`)), ((message === null || message === void 0 ? void 0 : message.ai) || !!((_b = message === null || message === void 0 ? void 0 : message.ai_files) === null || _b === void 0 ? void 0 : _b.length)) && (jsxs("div", { className: `p-3 rounded-lg bg-white mr-4`, children: [jsx("div", { className: "flex justify-between items-start mb-1", children: jsx("span", { className: "font-medium text-sm", children: t('analyticsAnalyticsTranscriptsStats.roleAgent') }) }), message.ai && jsx(Markdown, { children: getMessageContent(message.ai) }), jsx(TranscriptFiles, { files: message.ai_files }), jsx(TranscriptTurnDetails, { turn: message, ...turnDetails })] }, `${index}-ai`))] }, index));
|
|
233798
|
+
return (jsxs(React__default.Fragment, { children: [((message === null || message === void 0 ? void 0 : message.human) || !!((_a = message === null || message === void 0 ? void 0 : message.human_files) === null || _a === void 0 ? void 0 : _a.length)) && (jsxs("div", { className: `p-3 rounded-lg bg-blue-100 ml-4`, children: [jsx("div", { className: "flex justify-between items-start mb-1", children: jsx("span", { className: "font-medium text-sm", children: t('analyticsAnalyticsTranscriptsStats.roleUser') }) }), message.human && (jsx("p", { className: "text-sm", children: getMessageContent(message.human) })), jsx(TranscriptFiles, { files: message.human_files })] }, `${index}-human`)), ((message === null || message === void 0 ? void 0 : message.ai) || !!((_b = message === null || message === void 0 ? void 0 : message.ai_files) === null || _b === void 0 ? void 0 : _b.length)) && (jsxs("div", { className: `p-3 rounded-lg bg-white mr-4`, children: [jsx("div", { className: "flex justify-between items-start mb-1", children: jsx("span", { className: "font-medium text-sm", children: t('analyticsAnalyticsTranscriptsStats.roleAgent') }) }), message.ai && jsx(Markdown, { children: getMessageContent(message.ai) }), jsx(TranscriptFiles, { files: message.ai_files }), jsx(TranscriptTurnDetails, { turn: message, tenantKey: tenantKey, mentorId: detailMentorId, ...turnDetails })] }, `${index}-ai`))] }, index));
|
|
233746
233799
|
})) : (jsx("div", { className: "text-center text-gray-500 py-8", children: jsx("p", { children: t('analyticsAnalyticsTranscriptsStats.noMessages') }) })) })] })) }));
|
|
233747
233800
|
};
|
|
233748
|
-
return (jsxs("div", { className: "space-y-8", children: [jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 mb-6", children: [jsx(StatCard, { title: t('analyticsAnalyticsTranscriptsStats.avgMessagesPerConversation'), value: ((
|
|
233749
|
-
undefined, loading: isLoadingTranscriptsConversationHeadlineData, description: t('analyticsAnalyticsTranscriptsStats.avgMessagesPerConversationDescription') }), jsx(StatCard, { title: t('analyticsAnalyticsTranscriptsStats.avgCostPerConversation'), value: Number(((
|
|
233750
|
-
undefined, showDollarSign: true, loading: isLoadingTranscriptsConversationHeadlineData, description: t('analyticsAnalyticsTranscriptsStats.avgCostPerConversationDescription') }), jsx(StatCard, { title: t('analyticsAnalyticsTranscriptsStats.avgRating'), value: ((
|
|
233801
|
+
return (jsxs("div", { className: "space-y-8", children: [jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 mb-6", children: [jsx(StatCard, { title: t('analyticsAnalyticsTranscriptsStats.avgMessagesPerConversation'), value: ((_b = transcriptsConversationHeadlineData === null || transcriptsConversationHeadlineData === void 0 ? void 0 : transcriptsConversationHeadlineData.avg_messages_per_conversation) === null || _b === void 0 ? void 0 : _b.value) || 0, percentage: ((_c = transcriptsConversationHeadlineData === null || transcriptsConversationHeadlineData === void 0 ? void 0 : transcriptsConversationHeadlineData.avg_messages_per_conversation) === null || _c === void 0 ? void 0 : _c.percentage_change) ||
|
|
233802
|
+
undefined, loading: isLoadingTranscriptsConversationHeadlineData, description: t('analyticsAnalyticsTranscriptsStats.avgMessagesPerConversationDescription') }), jsx(StatCard, { title: t('analyticsAnalyticsTranscriptsStats.avgCostPerConversation'), value: Number(((_d = transcriptsConversationHeadlineData === null || transcriptsConversationHeadlineData === void 0 ? void 0 : transcriptsConversationHeadlineData.avg_cost_per_conversation) === null || _d === void 0 ? void 0 : _d.value) || 0).toFixed(2), percentage: ((_e = transcriptsConversationHeadlineData === null || transcriptsConversationHeadlineData === void 0 ? void 0 : transcriptsConversationHeadlineData.avg_cost_per_conversation) === null || _e === void 0 ? void 0 : _e.percentage_change) ||
|
|
233803
|
+
undefined, showDollarSign: true, loading: isLoadingTranscriptsConversationHeadlineData, description: t('analyticsAnalyticsTranscriptsStats.avgCostPerConversationDescription') }), jsx(StatCard, { title: t('analyticsAnalyticsTranscriptsStats.avgRating'), value: ((_f = transcriptsConversationHeadlineData === null || transcriptsConversationHeadlineData === void 0 ? void 0 : transcriptsConversationHeadlineData.avg_rating) === null || _f === void 0 ? void 0 : _f.value) || 0, percentage: ((_g = transcriptsConversationHeadlineData === null || transcriptsConversationHeadlineData === void 0 ? void 0 : transcriptsConversationHeadlineData.avg_rating) === null || _g === void 0 ? void 0 : _g.percentage_change) || undefined, loading: isLoadingTranscriptsConversationHeadlineData, description: t('analyticsAnalyticsTranscriptsStats.avgRatingDescription') })] }), jsxs("div", { className: "flex h-[calc(100vh-120px)]", children: [jsxs("div", { className: "w-full md:w-2/3 md:pr-6 overflow-y-auto md:border-r border-gray-200 [&::-webkit-scrollbar]:hidden [-ms-overflow-style:none] [scrollbar-width:none]", children: [jsx("div", { className: "mb-6", children: jsxs("div", { className: "flex gap-2 mb-4 items-center", children: [jsxs("div", { className: "relative", children: [jsx(Search, { className: "absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 h-4 w-4" }), jsx(Input, { placeholder: t('analyticsAnalyticsTranscriptsStats.placeholderTopics'), value: filters.topicSearch, onChange: (e) => setFilters({ ...filters, topicSearch: e.target.value }), className: "pl-10 w-[200px]" })] }), jsx("div", { className: "w-[200px]", children: jsx(UserFilterSelect, { tenantKey: tenantKey, value: filters.userSearch, onChange: (username) => setFilters({ ...filters, userSearch: username }), showSelfOption: false, className: "bg-transparent" }) }), isFetchingTranscriptsMessagesData && (filters.userSearch || filters.topicSearch) ? (jsx("div", { className: "animate-spin rounded-full h-5 w-5 border-b-2 border-blue-500 ml-2", "aria-hidden": "true" })) : (jsx(Button$1, { variant: "outline", onClick: handleClearFilters, className: "whitespace-nowrap", children: t('analyticsAnalyticsTranscriptsStats.clearAll') }))] }) }), jsxs("div", { className: "bg-gray-50 p-4 rounded-lg mb-6", children: [jsxs("div", { className: "flex flex-wrap gap-x-8 gap-y-2 text-sm text-gray-600", children: [jsxs("div", { "aria-label": t('analyticsAnalyticsTranscriptsStats.ariaTotalConversations'), children: [transcriptsMessagesData === null || transcriptsMessagesData === void 0 ? void 0 : transcriptsMessagesData.summary.total_conversations, ' ', t('analyticsAnalyticsTranscriptsStats.conversations')] }), jsx("div", { children: "\u2022" }), jsxs("div", { children: [transcriptsMessagesData === null || transcriptsMessagesData === void 0 ? void 0 : transcriptsMessagesData.summary.user_queries, ' ', t('analyticsAnalyticsTranscriptsStats.userQueries')] }), jsx("div", { children: "\u2022" }), jsxs("div", { children: [transcriptsMessagesData === null || transcriptsMessagesData === void 0 ? void 0 : transcriptsMessagesData.summary.assistant_responses, ' ', t('analyticsAnalyticsTranscriptsStats.assistantResponses')] })] }), jsx("div", { className: "flex flex-wrap gap-x-8 gap-y-2 text-sm text-gray-600 mt-2", children: jsxs("div", { children: [transcriptsMessagesData === null || transcriptsMessagesData === void 0 ? void 0 : transcriptsMessagesData.summary.average_sentiment_score, ' ', t('analyticsAnalyticsTranscriptsStats.averageSentimentScore')] }) })] }), jsx("div", { className: "flex flex-col gap-4", children: isLoadingTranscriptsMessagesData ? (
|
|
233751
233804
|
// Loading state
|
|
233752
233805
|
jsx("div", { className: "space-y-4", children: Array.from({ length: 3 }).map((_, index) => (jsxs(Card$1, { className: "p-4 animate-pulse", children: [jsx("div", { className: "mb-3", children: jsx("div", { className: "h-4 bg-gray-200 rounded w-3/4" }) }), jsxs("div", { className: "flex flex-wrap gap-2 mb-3", children: [jsx("div", { className: "h-6 bg-gray-200 rounded w-16" }), jsx("div", { className: "h-6 bg-gray-200 rounded w-20" })] }), jsxs("div", { className: "flex flex-wrap gap-x-6 gap-y-2 mb-2", children: [jsx("div", { className: "h-4 bg-gray-200 rounded w-24" }), jsx("div", { className: "h-4 bg-gray-200 rounded w-20" })] }), jsxs("div", { className: "flex flex-wrap gap-x-6 gap-y-2", children: [jsx("div", { className: "h-4 bg-gray-200 rounded w-20" }), jsx("div", { className: "h-4 bg-gray-200 rounded w-24" })] })] }, index))) })) : (transcriptsMessagesData === null || transcriptsMessagesData === void 0 ? void 0 : transcriptsMessagesData.results) && transcriptsMessagesData.results.length > 0 ? (
|
|
233753
233806
|
// Results
|
|
@@ -233768,6 +233821,8 @@ function AnalyticsTranscriptsStats({ tenantKey, currentSPA, mentorId, selectedMe
|
|
|
233768
233821
|
? ((_c = turnDetails === null || turnDetails === void 0 ? void 0 : turnDetails.renderModel) !== null && _c !== void 0 ? _c : renderModelBadge)({
|
|
233769
233822
|
model: transcript.model,
|
|
233770
233823
|
provider: providerOf(transcript),
|
|
233824
|
+
tenantKey,
|
|
233825
|
+
mentorId: (transcript === null || transcript === void 0 ? void 0 : transcript.mentor_unique_id) || currentMentorId,
|
|
233771
233826
|
})
|
|
233772
233827
|
: '-'] }) }), jsxs("div", { className: "flex flex-wrap gap-x-6 gap-y-2 text-xs text-gray-500", children: [jsxs("div", { children: [transcript.message_count, " ", t('analyticsAnalyticsTranscriptsStats.messages')] }), jsx("div", { children: "\u2022" }), jsxs("div", { children: ["$", Number((transcript === null || transcript === void 0 ? void 0 : transcript.cost) || 0).toFixed(2), ' ', t('analyticsAnalyticsTranscriptsStats.estimatedCost')] }), (transcript === null || transcript === void 0 ? void 0 : transcript.created_at) && (jsxs(Fragment$1, { children: [jsx("div", { children: "\u2022" }), jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.created'), ' ', formatRelativeTime(transcript.created_at, t)] })] }))] })] }, transcript.session));
|
|
233773
233828
|
})) : (
|
|
@@ -233805,14 +233860,16 @@ function AnalyticsTranscriptsStats({ tenantKey, currentSPA, mentorId, selectedMe
|
|
|
233805
233860
|
/* Error or no data state */
|
|
233806
233861
|
jsx("div", { className: "flex items-center justify-center h-full text-gray-400", children: jsxs("div", { className: "text-center", children: [jsx(X$1, { className: "h-6 w-6 mx-auto mb-2 text-red-400" }), jsx("p", { children: t('analyticsAnalyticsTranscriptsStats.noTranscriptDetails') }), jsx("p", { className: "text-sm mt-1", children: t('analyticsAnalyticsTranscriptsStats.unableToLoadTranscript') })] }) })) : (
|
|
233807
233862
|
/* Data loaded successfully */
|
|
233808
|
-
jsxs("div", { className: "space-y-4", children: [jsx("div", { className: "mb-4", children: jsxs("div", { className: "text-sm text-gray-500 space-y-1", children: [jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelUser'), ' ', jsx(UserProfileLink, { currentSPA: currentSPA, tenantKey: tenantKey, username: (
|
|
233809
|
-
? ((
|
|
233863
|
+
jsxs("div", { className: "space-y-4", children: [jsx("div", { className: "mb-4", children: jsxs("div", { className: "text-sm text-gray-500 space-y-1", children: [jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelUser'), ' ', jsx(UserProfileLink, { currentSPA: currentSPA, tenantKey: tenantKey, username: (_h = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _h === void 0 ? void 0 : _h.username, display: identifyUser(transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) })] }), jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelAgent'), ' ', jsx(AgentScopeLink, { uniqueId: (_j = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _j === void 0 ? void 0 : _j.mentor_unique_id, name: (_k = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _k === void 0 ? void 0 : _k.mentor })] }), jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [t('analyticsAnalyticsTranscriptsStats.labelModel'), ' ', ((_l = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _l === void 0 ? void 0 : _l.model)
|
|
233864
|
+
? ((_m = turnDetails === null || turnDetails === void 0 ? void 0 : turnDetails.renderModel) !== null && _m !== void 0 ? _m : renderModelBadge)({
|
|
233810
233865
|
model: transcriptsMessagesDetailsData.summary.model,
|
|
233811
|
-
provider: (
|
|
233866
|
+
provider: (_o = providerOf(transcriptsMessagesDetailsData.summary)) !== null && _o !== void 0 ? _o : providerOfTurns(transcriptsMessagesDetailsData.messages),
|
|
233867
|
+
tenantKey,
|
|
233868
|
+
mentorId: detailMentorId,
|
|
233812
233869
|
})
|
|
233813
|
-
: '-'] }), jsx(TranscriptDocumentsButton, { detail: transcriptsMessagesDetailsData, className: "mt-2" })] }) }), jsx("div", { className: "space-y-4 pb-4", children: ((
|
|
233870
|
+
: '-'] }), jsx(TranscriptDocumentsButton, { detail: transcriptsMessagesDetailsData, className: "mt-2" })] }) }), jsx("div", { className: "space-y-4 pb-4", children: ((_p = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.messages) === null || _p === void 0 ? void 0 : _p.length) > 0 ? ((_q = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.messages) === null || _q === void 0 ? void 0 : _q.map((message, index) => {
|
|
233814
233871
|
var _a, _b;
|
|
233815
|
-
return (jsxs(React__default.Fragment, { children: [((message === null || message === void 0 ? void 0 : message.human) || !!((_a = message === null || message === void 0 ? void 0 : message.human_files) === null || _a === void 0 ? void 0 : _a.length)) && (jsxs("div", { className: "p-3 rounded-lg bg-blue-100 ml-4", children: [jsx("div", { className: "flex justify-between items-start mb-1", children: jsx("span", { className: "font-medium text-sm", children: t('analyticsAnalyticsTranscriptsStats.roleUser') }) }), message.human && (jsx("p", { className: "text-sm", children: getMessageContent(message.human) })), jsx(TranscriptFiles, { files: message.human_files })] })), ((message === null || message === void 0 ? void 0 : message.ai) || !!((_b = message === null || message === void 0 ? void 0 : message.ai_files) === null || _b === void 0 ? void 0 : _b.length)) && (jsxs("div", { className: "p-3 rounded-lg bg-white mr-4 border", style: { borderColor: 'oklch(.922 0 0)' }, children: [jsx("div", { className: "flex justify-between items-start mb-1", children: jsx("span", { className: "font-medium text-sm", children: t('analyticsAnalyticsTranscriptsStats.roleAgent') }) }), message.ai && jsx(Markdown, { children: getMessageContent(message.ai) }), jsx(TranscriptFiles, { files: message.ai_files }), jsx(TranscriptTurnDetails, { turn: message, ...turnDetails })] }))] }, index));
|
|
233872
|
+
return (jsxs(React__default.Fragment, { children: [((message === null || message === void 0 ? void 0 : message.human) || !!((_a = message === null || message === void 0 ? void 0 : message.human_files) === null || _a === void 0 ? void 0 : _a.length)) && (jsxs("div", { className: "p-3 rounded-lg bg-blue-100 ml-4", children: [jsx("div", { className: "flex justify-between items-start mb-1", children: jsx("span", { className: "font-medium text-sm", children: t('analyticsAnalyticsTranscriptsStats.roleUser') }) }), message.human && (jsx("p", { className: "text-sm", children: getMessageContent(message.human) })), jsx(TranscriptFiles, { files: message.human_files })] })), ((message === null || message === void 0 ? void 0 : message.ai) || !!((_b = message === null || message === void 0 ? void 0 : message.ai_files) === null || _b === void 0 ? void 0 : _b.length)) && (jsxs("div", { className: "p-3 rounded-lg bg-white mr-4 border", style: { borderColor: 'oklch(.922 0 0)' }, children: [jsx("div", { className: "flex justify-between items-start mb-1", children: jsx("span", { className: "font-medium text-sm", children: t('analyticsAnalyticsTranscriptsStats.roleAgent') }) }), message.ai && jsx(Markdown, { children: getMessageContent(message.ai) }), jsx(TranscriptFiles, { files: message.ai_files }), jsx(TranscriptTurnDetails, { turn: message, tenantKey: tenantKey, mentorId: detailMentorId, ...turnDetails })] }))] }, index));
|
|
233816
233873
|
})) : (jsx("div", { className: "text-center text-gray-500 py-8", children: jsx("p", { children: t('analyticsAnalyticsTranscriptsStats.noMessages') }) })) })] })) })] }) }))] }));
|
|
233817
233874
|
}
|
|
233818
233875
|
|
|
@@ -236507,7 +236564,10 @@ function AgentAuditLogView({ tenantKey, mentorId, userId, selectedMentorId, curr
|
|
|
236507
236564
|
}, numberOfMonths: 2 }) })] }), jsx("div", { className: "min-w-[160px]", children: jsxs(Select$1, { value: actionFilter !== undefined ? String(actionFilter) : 'all', onValueChange: (val) => {
|
|
236508
236565
|
setActionFilter(val === 'all' ? undefined : Number(val));
|
|
236509
236566
|
setPage(1);
|
|
236510
|
-
}, children: [jsx(SelectTrigger, { className: "w-full font-normal bg-white h-9", children: jsx(SelectValue, { placeholder: t('analyticsAnalyticsAuditLogStats.allActions') }) }), jsx(SelectContent, { children: ACTION_FILTER_OPTIONS.map((opt) => (jsx(SelectItem, { value: opt.value, children: opt.label }, opt.value))) })] }) })] }) }), jsx("div", { className: "overflow-hidden rounded-md border border-[oklch(0.922_0_0)] bg-white", children: jsx("div", { className: "overflow-x-auto", children: jsx("div", { className: "inline-block min-w-full align-middle", children: isLoading ? (jsx("div", { className: "flex items-center justify-center w-full py-10", children: jsx("div", { className: "h-6 w-6 animate-spin rounded-full border-2 border-blue-600 border-t-transparent" }) })) : error && !is403 ? (jsx("div", { className: "py-8 text-center text-[#646464]", children: t('analyticsAnalyticsAuditLogStats.loadError') })) : !((_a = data === null || data === void 0 ? void 0 : data.results) === null || _a === void 0 ? void 0 : _a.length) ? (jsx("div", { className: "h-[120px]", children: jsx(EmptyStats, { title: t('analyticsAnalyticsAuditLogStats.noEntries') }) })) : (jsxs(Table, { className: "min-w-full", children: [jsx(TableHeader, { children: jsxs(TableRow, { className: "bg-muted/50 border-b border-[oklch(0.922_0_0)]", children: [jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: t('analyticsAnalyticsAuditLogStats.colUser') }), jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: t('analyticsAnalyticsAuditLogStats.colAction') }), jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: t('analyticsAnalyticsAuditLogStats.colTime') })] }) }), jsx(TableBody, { children: data.results.map((entry) => (jsxs(TableRow, { className: "border-b border-[oklch(0.922_0_0)] last:border-0", children: [jsx(TableCell, { className: "p-3 font-medium whitespace-nowrap text-[#646464]", children: jsx(UserProfileLink, { tenantKey: tenantKey, currentSPA: currentSPA, username: entry.actor_username, display:
|
|
236567
|
+
}, children: [jsx(SelectTrigger, { className: "w-full font-normal bg-white h-9", children: jsx(SelectValue, { placeholder: t('analyticsAnalyticsAuditLogStats.allActions') }) }), jsx(SelectContent, { children: ACTION_FILTER_OPTIONS.map((opt) => (jsx(SelectItem, { value: opt.value, children: opt.label }, opt.value))) })] }) })] }) }), jsx("div", { className: "overflow-hidden rounded-md border border-[oklch(0.922_0_0)] bg-white", children: jsx("div", { className: "overflow-x-auto", children: jsx("div", { className: "inline-block min-w-full align-middle", children: isLoading ? (jsx("div", { className: "flex items-center justify-center w-full py-10", children: jsx("div", { className: "h-6 w-6 animate-spin rounded-full border-2 border-blue-600 border-t-transparent" }) })) : error && !is403 ? (jsx("div", { className: "py-8 text-center text-[#646464]", children: t('analyticsAnalyticsAuditLogStats.loadError') })) : !((_a = data === null || data === void 0 ? void 0 : data.results) === null || _a === void 0 ? void 0 : _a.length) ? (jsx("div", { className: "h-[120px]", children: jsx(EmptyStats, { title: t('analyticsAnalyticsAuditLogStats.noEntries') }) })) : (jsxs(Table, { className: "min-w-full", children: [jsx(TableHeader, { children: jsxs(TableRow, { className: "bg-muted/50 border-b border-[oklch(0.922_0_0)]", children: [jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: t('analyticsAnalyticsAuditLogStats.colUser') }), jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: t('analyticsAnalyticsAuditLogStats.colAction') }), jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: t('analyticsAnalyticsAuditLogStats.colTime') })] }) }), jsx(TableBody, { children: data.results.map((entry) => (jsxs(TableRow, { className: "border-b border-[oklch(0.922_0_0)] last:border-0", children: [jsx(TableCell, { className: "p-3 font-medium whitespace-nowrap text-[#646464]", children: jsx(UserProfileLink, { tenantKey: tenantKey, currentSPA: currentSPA, username: entry.actor_username, display: resolveUserIdentity({
|
|
236568
|
+
email: entry.actor_email,
|
|
236569
|
+
username: entry.actor_username,
|
|
236570
|
+
}).label }) }), jsx(TableCell, { className: "min-w-[250px] p-3 text-[#646464]", children: summarizeChanges(entry) }), jsx(TableCell, { className: "p-3 whitespace-nowrap text-[#646464]", children: formatDistanceToNow(new Date(entry.timestamp), { addSuffix: true }) })] }, entry.id))) })] })) }) }) }), jsx(IblPagination, { currentPage: page, totalPages: totalPages, onPageChange: setPage, disabled: isLoading })] }));
|
|
236511
236571
|
}
|
|
236512
236572
|
function AnalyticsAuditLogStats({ tenantKey, currentSPA, mentorId, userId, selectedMentorId, defaultScope = 'agent', timezone, canViewPlatformAudit, }) {
|
|
236513
236573
|
const t = useT();
|
|
@@ -236554,7 +236614,7 @@ function toRowFromMentorMemory(memory) {
|
|
|
236554
236614
|
content: memory.content,
|
|
236555
236615
|
categoryName: (_b = (_a = memory.category) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : null,
|
|
236556
236616
|
categorySlug: (_d = (_c = memory.category) === null || _c === void 0 ? void 0 : _c.slug) !== null && _d !== void 0 ? _d : null,
|
|
236557
|
-
learner: memory.
|
|
236617
|
+
learner: resolveUserIdentity(memory).label,
|
|
236558
236618
|
username: memory.username,
|
|
236559
236619
|
email: memory.email || null,
|
|
236560
236620
|
mentorId: (_e = memory.mentor_id) !== null && _e !== void 0 ? _e : null,
|
|
@@ -236704,7 +236764,7 @@ const useMemory = ({ tenantKey, userId, mentorId, myMemory = false, }) => {
|
|
|
236704
236764
|
let totalMemories = 0;
|
|
236705
236765
|
const categories = groups.map((group) => {
|
|
236706
236766
|
group.memories.forEach((memory) => {
|
|
236707
|
-
learners.add(memory.
|
|
236767
|
+
learners.add(resolveUserIdentity(memory).label);
|
|
236708
236768
|
if (memory.is_auto_generated)
|
|
236709
236769
|
autoCaptured += 1;
|
|
236710
236770
|
});
|
|
@@ -248212,5 +248272,5 @@ var trainOrDeleteModal = /*#__PURE__*/Object.freeze({
|
|
|
248212
248272
|
TrainOrDeleteModal: TrainOrDeleteModal
|
|
248213
248273
|
});
|
|
248214
248274
|
|
|
248215
|
-
export { ACCESS_COURSE_LABEL, ALL_CATEGORIES, AccessTimeHeatmap, AccessiblePaginate, AddMentorToProjectModal, AddSkillDialog, Admin, AdvancedTab, AgentConfigPrompts, AgentScopeLink, AgentSkills, AlertsTab, AnalyticsAgentPicker, AnalyticsAreaChart, AnalyticsAuditLogStats, AnalyticsBarChart, AnalyticsControlBar, AnalyticsCourseDetail, AnalyticsCourses, AnalyticsFinancialStats, AnalyticsGlobalFiltersProvider, AnalyticsLayout, AnalyticsLlmUsageStats, AnalyticsMemoryStats, AnalyticsMonetizationStats, AnalyticsNotice, AnalyticsOverview, AnalyticsPlatformAuditLog, AnalyticsProgramDetail, AnalyticsPrograms, AnalyticsReportDownload, AnalyticsReports, AnalyticsScopeProvider, AnalyticsSettingsProvider, AnalyticsTopicsStats, AnalyticsTraces, AnalyticsTranscriptsStats, AnalyticsUsersStats, AppleRestrictionModal, ApplyWizard, AuditLogChangesView, AuditLogJsonView, BUY_NOW_LABEL, BillingTab, CATALOGS, CHART_ORDINAL_RAMP, CHART_SERIES_COLORS, CHART_SINGLE_SERIES_COLOR, CREATE_AGENT_LABELS, CUA_DRIVER_REPO_URL, CUA_DRIVER_TAURI_COMMANDS, CUA_DRIVER_TAURI_EVENTS, CardInfoIcon, CategorizedDropdownMenu, ChartCardWrapper, ChartFiltersProvider, ChartLoading, ChartTooltipCard, ChatContext, ChatPrivacyToggle, ChatProvider, CompanyDialog, ConnectorManagementDialog, CopyButtonIcon, CourseAccessGuard, CourseCardSkeleton, CourseContentLoading, CourseOutline, CourseOutlineContext, CourseOutlineDrawer, CreateAgentForm, CreateAgentModal, CreateProjectModal, CreateWorkflowModal, CredentialBox, CredentialMiniBoxSkeleton, CredentialsList, CreditBalance, CustomDateRangePicker, CustomTooltip, DEFAULT_COWORK_REQUIRED_SIZE_GB, DEFAULT_LOCALE, DEFAULT_USER_IDENTITY_ORDER, DefaultEmptyBox, DeleteProjectModal, DeleteWorkflowModal, DiscoverContentCard, DiscoverFacetsFilter, DiscoverFilterDrawer, DocumentSidebar, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, ENROLL_NOW_COURSE_STARTING_SOON_LABEL, ENROLL_NOW_LABEL, EditAlertDialog, EducationBox, EducationDialog, EducationTab, EdxIframeContext, EmptyStats, ExperienceBox, ExperienceDialog, ExperienceTab, FacetFilterContext, Footer, GradebookTab, GroupsFilterDropdown, INVITATION_ONLY_LABEL, InstitutionDialog, IntegrationsTab, InviteUserContent, InviteUserDialog, InvitedUsersDialog, LOCAL_LLM_CHANGED_EVENT, LOCAL_MODELS, LlmModelBadge, Loader, LocalLLMTab, LocalModelsContent, LoginButton, MACOS_PERMISSIONS_COMMANDS, MEMORY_PAGE_SIZE, Markdown, MemoryAdminTab, MinimumMentorAlert, MonetizationTab, NotificationDisplay, NotificationDropdown, ONBOARDING_FIELD_TYPES, ONBOARDING_SECTORS, OnboardingField, OnboardingShell, OnboardingWizard, PLATFORM_AUDIT_DEEP_PAGING_THRESHOLD, PLATFORM_AUDIT_PAGE_SIZE, PaywallModal, PlacementExam, PlacementExamsDropdown, PlatformAuditLogDetailSheet, PlatformNavbar, PlatformNavbarSearch, Profile, ProfileTimeChart, ProgressDots, ProjectActionButtons, ProjectFilesModal, ProjectInstructionsModal, ProjectLandingPage, ProjectMentorsList, ProjectsPage, REQUEST_ACCESS_COURSE_STARTING_SOON_LABEL, REQUEST_ACCESS_LABEL, RankedBars, RenameProjectModal, ResumeBox, ResumeTab, RetrievedDocumentsButton, RichTextEditor, SERVICE_COLOR_ORDER, SUPPORTED_LOCALES, SandboxConfig, SearchableMultiSelect, SendNotificationDialog, SignupButton, SkeletonActivityStatBox, SkeletonAddSkillsLoading, SkeletonCreatePathwaySearchList, SkeletonDiscoverFilterBox, SkeletonEducationBox, SkeletonMultiplier, SkeletonPathwayBox, SkeletonProfileInfoCard, SkeletonSkillBox, SkillBox, SkillDetailModal, SkillLeaderboardChart, SkillsBox, SkillsList, SlashSkillPicker, SourceChip, Spinner, StatCard, StepHeader, SummerNoteTextEditor, TAURI_COMMANDS, TAURI_EVENTS, TRACES_PAGE_SIZE, TenantSwitcher, TimeFilter, TimeTrackingProvider, TimedExam, ToolCallIndicator, ToolDialogs, TopBanner, TranscriptFileCards, TranscriptRollupBadges, TranscriptTurnDetails, TrendSparkline, USER_ONBOARDING_FORM_SLUG, USER_ONBOARDING_FORM_VERSION, USER_ONBOARDING_SUBMISSION_SLUG, UpgradePackageModal, UserAvatar, UserOnboardingWizard, UserProfileDialog, UserProfileLink, Version, WebContainersI18nProvider, WorkflowSidebar, addBookmarksTab, areChartFiltersEqual, availablePlacementExams, buildOnboardingAgentPath, buildUserOnboardingFormMetadata, buildUserOnboardingSubmissionMetadata, coerceOnboardingAgent, coerceOnboardingAgents, coerceUserOnboardingForm, colorForRank, colorForSeries, conversationDocuments, emptyUserOnboardingForm, findLastResumeBlock, findOnboardingAgent, findSequentialParent, flattenVerticalBlocks, formatAuditTimestamp, formatCompactNumber, formatFileSize, formatLatencySeconds, formatResult, formatTimeZoneLabel, getCatalog, getFirstAvailableUnit, getFriendlyToolName, getLLMModelDisplayName, getLocalLLMModel, getLocalLLMToolSupport, getNextUnitIframe, getOrg, getParentBlockById, getParentsInfosFromSublessonId, getPreviousUnitIframe, getQueryLabel, getRandomCourseImage, getSectorById, getTenant, getUnitToIframe, getUserId, getUserName, granularityForFilter, hasOnboardingContent, humanizeAuditToken, humanizeFieldName, inBrowserPrint, inIframe, inferProviderKeysFromModel, initialCuaDriverInstallState, initialModelDownloadState, isCoworkEnabled, isEmailLike, isFileReference, isLlmUsageForbidden, isLlmUsageUnavailable, isLocalLLMEnabled, isPlatformNavbarCreditBalanceVisible, isSlashCommandToken, isTauriApp, isValidLocale, isValidTimeZone, llmUsageDateArgs, components as markdownComponents, modelSupportsCowork, onboardingPrimaryButtonClass, onboardingSecondaryButtonClass, readUserOnboardingForm, readUserOnboardingSubmission, readUserOnboardingSubmissions, resolveAuditTimeZone, resolveCreateAgentLabels, resolveUserIdentity, sanitizeCss, setCoworkEnabled, setLocalLLMEnabled, setLocalLLMModel, setLocalLLMToolSupport, smallestCoworkModel, summarizeTranscriptTurns, toFileReference, toRetrievedDocuments, toToolCallInfos, useAnalyticsGlobalFilters, useAnalyticsScope, useAnalyticsSettings, useAuditLog, useCatalogSearch, useChartFilters, useChatPrivacy, useChatState, useCourseDetail, useCourseMetadata, useCourses, useCuaDriver, useDiscover, useEdxIframe, useFinancial, useIframeMessageHandler, useLlmDisplayName, useLlmProviderCatalogue, useLlmTraces, useLlmUsage, useLocalStorage, useMemory, useModelDownload, useMonetization, useOverview, usePlatformAuditLabels, usePlatformAuditLog, useProfileActivityStats, useProfileCredentials, useProfilePathways, useProfilePrograms, useProfileSkills, useProfileTimeSpent, usePrograms, useReports, useScopedMentorDbId, useScopedMentorId, useSelectedLocalModel, useSlashSkillPicker, useSlashSkills, useT, useTauri, useTimeTracking, useTopics, useTranscripts, useUserCourses, useUserMetadata, useUsers, useWebContainersI18n, useWebContainersLocale };
|
|
248275
|
+
export { ACCESS_COURSE_LABEL, ALL_CATEGORIES, AccessTimeHeatmap, AccessiblePaginate, AddMentorToProjectModal, AddSkillDialog, Admin, AdvancedTab, AgentConfigPrompts, AgentScopeLink, AgentSkills, AlertsTab, AnalyticsAgentPicker, AnalyticsAreaChart, AnalyticsAuditLogStats, AnalyticsBarChart, AnalyticsControlBar, AnalyticsCourseDetail, AnalyticsCourses, AnalyticsFinancialStats, AnalyticsGlobalFiltersProvider, AnalyticsLayout, AnalyticsLlmUsageStats, AnalyticsMemoryStats, AnalyticsMonetizationStats, AnalyticsNotice, AnalyticsOverview, AnalyticsPlatformAuditLog, AnalyticsProgramDetail, AnalyticsPrograms, AnalyticsReportDownload, AnalyticsReports, AnalyticsScopeProvider, AnalyticsSettingsProvider, AnalyticsTopicsStats, AnalyticsTraces, AnalyticsTranscriptsStats, AnalyticsUsersStats, AppleRestrictionModal, ApplyWizard, AuditLogChangesView, AuditLogJsonView, BUY_NOW_LABEL, BillingTab, CATALOGS, CHART_ORDINAL_RAMP, CHART_SERIES_COLORS, CHART_SINGLE_SERIES_COLOR, CREATE_AGENT_LABELS, CUA_DRIVER_REPO_URL, CUA_DRIVER_TAURI_COMMANDS, CUA_DRIVER_TAURI_EVENTS, CardInfoIcon, CategorizedDropdownMenu, ChartCardWrapper, ChartFiltersProvider, ChartLoading, ChartTooltipCard, ChatContext, ChatPrivacyToggle, ChatProvider, CompanyDialog, ConnectorManagementDialog, CopyButtonIcon, CourseAccessGuard, CourseCardSkeleton, CourseContentLoading, CourseOutline, CourseOutlineContext, CourseOutlineDrawer, CreateAgentForm, CreateAgentModal, CreateProjectModal, CreateWorkflowModal, CredentialBox, CredentialMiniBoxSkeleton, CredentialsList, CreditBalance, CustomDateRangePicker, CustomTooltip, DEFAULT_COWORK_REQUIRED_SIZE_GB, DEFAULT_LOCALE, DEFAULT_USER_IDENTITY_ORDER, DefaultEmptyBox, DeleteProjectModal, DeleteWorkflowModal, DiscoverContentCard, DiscoverFacetsFilter, DiscoverFilterDrawer, DocumentSidebar, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, ENROLL_NOW_COURSE_STARTING_SOON_LABEL, ENROLL_NOW_LABEL, EditAlertDialog, EducationBox, EducationDialog, EducationTab, EdxIframeContext, EmptyStats, ExperienceBox, ExperienceDialog, ExperienceTab, FacetFilterContext, Footer, GradebookTab, GroupsFilterDropdown, INVITATION_ONLY_LABEL, InstitutionDialog, IntegrationsTab, InviteUserContent, InviteUserDialog, InvitedUsersDialog, LOCAL_LLM_CHANGED_EVENT, LOCAL_MODELS, LlmModelBadge, Loader, LocalLLMTab, LocalModelsContent, LoginButton, MACOS_PERMISSIONS_COMMANDS, MEMORY_PAGE_SIZE, Markdown, MemoryAdminTab, MinimumMentorAlert, MonetizationTab, NotificationDisplay, NotificationDropdown, ONBOARDING_FIELD_TYPES, ONBOARDING_SECTORS, OnboardingField, OnboardingShell, OnboardingWizard, PLATFORM_AUDIT_DEEP_PAGING_THRESHOLD, PLATFORM_AUDIT_PAGE_SIZE, PaywallModal, PlacementExam, PlacementExamsDropdown, PlatformAuditLogDetailSheet, PlatformNavbar, PlatformNavbarSearch, Profile, ProfileTimeChart, ProgressDots, ProjectActionButtons, ProjectFilesModal, ProjectInstructionsModal, ProjectLandingPage, ProjectMentorsList, ProjectsPage, REQUEST_ACCESS_COURSE_STARTING_SOON_LABEL, REQUEST_ACCESS_LABEL, RankedBars, RenameProjectModal, ResumeBox, ResumeTab, RetrievedDocumentsButton, RichTextEditor, SERVICE_COLOR_ORDER, SUPPORTED_LOCALES, SandboxConfig, SearchableMultiSelect, SendNotificationDialog, SignupButton, SkeletonActivityStatBox, SkeletonAddSkillsLoading, SkeletonCreatePathwaySearchList, SkeletonDiscoverFilterBox, SkeletonEducationBox, SkeletonMultiplier, SkeletonPathwayBox, SkeletonProfileInfoCard, SkeletonSkillBox, SkillBox, SkillDetailModal, SkillLeaderboardChart, SkillsBox, SkillsList, SlashSkillPicker, SourceChip, Spinner, StatCard, StepHeader, SummerNoteTextEditor, TAURI_COMMANDS, TAURI_EVENTS, TRACES_PAGE_SIZE, TenantSwitcher, TimeFilter, TimeTrackingProvider, TimedExam, ToolCallIndicator, ToolDialogs, TopBanner, TranscriptFileCards, TranscriptRollupBadges, TranscriptTurnDetails, TrendSparkline, USER_ONBOARDING_FORM_SLUG, USER_ONBOARDING_FORM_VERSION, USER_ONBOARDING_SUBMISSION_SLUG, UpgradePackageModal, UserAvatar, UserOnboardingWizard, UserProfileDialog, UserProfileLink, Version, WebContainersI18nProvider, WorkflowSidebar, addBookmarksTab, areChartFiltersEqual, availablePlacementExams, buildOnboardingAgentPath, buildUserOnboardingFormMetadata, buildUserOnboardingSubmissionMetadata, coerceOnboardingAgent, coerceOnboardingAgents, coerceUserOnboardingForm, colorForRank, colorForSeries, conversationDocuments, emptyUserOnboardingForm, findLastResumeBlock, findOnboardingAgent, findSequentialParent, flattenVerticalBlocks, formatAuditTimestamp, formatCompactNumber, formatFileSize, formatLatencySeconds, formatResult, formatTimeZoneLabel, getCatalog, getFirstAvailableUnit, getFriendlyToolName, getLLMModelDisplayName, getLocalLLMModel, getLocalLLMToolSupport, getNextUnitIframe, getOrg, getParentBlockById, getParentsInfosFromSublessonId, getPreviousUnitIframe, getQueryLabel, getRandomCourseImage, getSectorById, getTenant, getUnitToIframe, getUserId, getUserName, granularityForFilter, hasDistinctName, hasOnboardingContent, humanizeAuditToken, humanizeFieldName, inBrowserPrint, inIframe, inferProviderKeysFromModel, initialCuaDriverInstallState, initialModelDownloadState, isCoworkEnabled, isEmailLike, isFileReference, isLlmUsageForbidden, isLlmUsageUnavailable, isLocalLLMEnabled, isPlatformNavbarCreditBalanceVisible, isSlashCommandToken, isTauriApp, isValidLocale, isValidTimeZone, llmUsageDateArgs, components as markdownComponents, modelSupportsCowork, onboardingPrimaryButtonClass, onboardingSecondaryButtonClass, readUserOnboardingForm, readUserOnboardingSubmission, readUserOnboardingSubmissions, resolveAuditTimeZone, resolveCreateAgentLabels, resolveUserIdentity, sanitizeCss, setCoworkEnabled, setLocalLLMEnabled, setLocalLLMModel, setLocalLLMToolSupport, smallestCoworkModel, summarizeTranscriptTurns, toFileReference, toRetrievedDocuments, toToolCallInfos, useAnalyticsGlobalFilters, useAnalyticsScope, useAnalyticsSettings, useAuditLog, useCatalogSearch, useChartFilters, useChatPrivacy, useChatState, useCourseDetail, useCourseMetadata, useCourses, useCuaDriver, useDiscover, useEdxIframe, useFinancial, useIframeMessageHandler, useLlmDisplayName, useLlmProviderCatalogue, useLlmTraces, useLlmUsage, useLocalStorage, useMemory, useModelDownload, useMonetization, useOverview, usePlatformAuditLabels, usePlatformAuditLog, useProfileActivityStats, useProfileCredentials, useProfilePathways, useProfilePrograms, useProfileSkills, useProfileTimeSpent, usePrograms, useReports, useScopedMentorDbId, useScopedMentorId, useSelectedLocalModel, useSlashSkillPicker, useSlashSkills, useT, useTauri, useTimeTracking, useTopics, useTranscripts, useUserCourses, useUserMetadata, useUsers, useWebContainersI18n, useWebContainersLocale };
|
|
248216
248276
|
//# sourceMappingURL=index.esm.js.map
|
|
@@ -221826,7 +221826,7 @@ function RetrievedDocumentsButton({ sessionId = '', documents, className, label,
|
|
|
221826
221826
|
// Safe inside clickable transcript rows: the row must not react.
|
|
221827
221827
|
event.stopPropagation();
|
|
221828
221828
|
setIsOpen(true);
|
|
221829
|
-
}, "data-testid": "retrieved-documents-button", children: [jsx(FileText, { className: "h-4 w-4 text-blue-600" }), jsx("span", { children: label !== null && label !== void 0 ? label : t('retrievedDocumentsButton.title') })] }), jsx(Dialog, { open: isOpen, onOpenChange: setIsOpen, children: jsxs(DialogContent, { className: "flex max-h-[80vh] flex-col gap-0 p-0 sm:max-w-[425px] md:max-w-[600px]", children: [jsx(DialogHeader, { className: "border-b px-4 py-2", children: jsxs(DialogTitle, { className: "flex items-center gap-2 text-base font-medium text-[#646464]", children: [jsx(FileText, { className: "h-5 w-5 text-blue-600" }), t('retrievedDocumentsButton.title')] }) }), jsx("div", { className: "min-h-0 flex-1 overflow-y-auto", "data-testid": "retrieved-documents-scroll", children: jsx(DocumentSidebar, { isModal: true, sessionId: sessionId, documents: documents }) })] }) })] }));
|
|
221829
|
+
}, "data-testid": "retrieved-documents-button", children: [jsx(FileText, { className: "h-4 w-4 text-blue-600" }), jsx("span", { children: label !== null && label !== void 0 ? label : t('retrievedDocumentsButton.title') })] }), jsx(Dialog, { open: isOpen, onOpenChange: setIsOpen, children: jsxs(DialogContent, { className: "flex max-h-[80vh] flex-col gap-0 p-0 sm:max-w-[425px] md:max-w-[600px]", children: [jsx(DialogHeader, { className: "border-b border-gray-200 px-4 py-2", children: jsxs(DialogTitle, { className: "flex items-center gap-2 text-base font-medium text-[#646464]", children: [jsx(FileText, { className: "h-5 w-5 text-blue-600" }), t('retrievedDocumentsButton.title')] }) }), jsx("div", { className: "min-h-0 flex-1 overflow-y-auto", "data-testid": "retrieved-documents-scroll", children: jsx(DocumentSidebar, { isModal: true, sessionId: sessionId, documents: documents }) })] }) })] }));
|
|
221830
221830
|
}
|
|
221831
221831
|
|
|
221832
221832
|
var COLLAPSIBLE_NAME = "Collapsible";
|
|
@@ -259773,6 +259773,154 @@ AnalyticsScopeContext.Provider;
|
|
|
259773
259773
|
|
|
259774
259774
|
Array.from({ length: 24 }, (_, i) => i);
|
|
259775
259775
|
|
|
259776
|
+
/**
|
|
259777
|
+
* One place that decides how a user is shown.
|
|
259778
|
+
*
|
|
259779
|
+
* Backend payloads name their user in a dozen ways — `email` / `lti_email` /
|
|
259780
|
+
* `user_email`, `username` / `student` / `lti_username`, `name` /
|
|
259781
|
+
* `full_name` / `user_full_name`, or a nested `user` object — and most of
|
|
259782
|
+
* them can be empty, `null`, or a placeholder like "None". Every surface used
|
|
259783
|
+
* to pick through those on its own. `resolveUserIdentity()` takes whatever
|
|
259784
|
+
* the payload is and returns what to show, what to open a profile on, and
|
|
259785
|
+
* the avatar initial, so the rule lives here and can be tweaked once.
|
|
259786
|
+
*
|
|
259787
|
+
* Default rule: a real full name first (see {@link hasDistinctName}), else
|
|
259788
|
+
* email → username → the anonymous label.
|
|
259789
|
+
*/
|
|
259790
|
+
const DEFAULT_USER_IDENTITY_ORDER = ['email', 'name', 'username'];
|
|
259791
|
+
/** Order used when the payload carries a real name (see {@link hasDistinctName}). */
|
|
259792
|
+
const NAME_FIRST_ORDER = ['name', 'email', 'username'];
|
|
259793
|
+
/**
|
|
259794
|
+
* Lower-case letters and digits of any script, accents and punctuation dropped,
|
|
259795
|
+
* so "Conrad Mugabe" / "conrad.mugabe" and "José" / "jose" compare equal.
|
|
259796
|
+
*/
|
|
259797
|
+
const identityKey = (value) => (value !== null && value !== void 0 ? value : '')
|
|
259798
|
+
.normalize('NFKD')
|
|
259799
|
+
.replace(/[^\p{L}\p{N}]/gu, '')
|
|
259800
|
+
.toLowerCase();
|
|
259801
|
+
/**
|
|
259802
|
+
* Whether `name` is a real name rather than a restatement of the username.
|
|
259803
|
+
*
|
|
259804
|
+
* Many payloads fill the name field with the username ("conradmugabe" →
|
|
259805
|
+
* "Conrad Mugabe", "fayyaz_4_ibleducation_com"), which tells an admin nothing
|
|
259806
|
+
* the email does not. A name that matches none of the usernames on the record
|
|
259807
|
+
* is the most recognisable thing on it — including for LTI learners, whose
|
|
259808
|
+
* minted `<opaque-id>@edx-lti-1p3.com` address and username identify nobody.
|
|
259809
|
+
*/
|
|
259810
|
+
function hasDistinctName(name, ...usernames) {
|
|
259811
|
+
const key = identityKey(name);
|
|
259812
|
+
if (!key)
|
|
259813
|
+
return false;
|
|
259814
|
+
return usernames.every((username) => key !== identityKey(username));
|
|
259815
|
+
}
|
|
259816
|
+
const DEFAULT_ANONYMOUS_LABEL = 'Anonymous';
|
|
259817
|
+
/** Values backends emit instead of "nothing". Compared case-insensitively. */
|
|
259818
|
+
const PLACEHOLDERS = new Set([
|
|
259819
|
+
'',
|
|
259820
|
+
'none',
|
|
259821
|
+
'null',
|
|
259822
|
+
'undefined',
|
|
259823
|
+
'n/a',
|
|
259824
|
+
'na',
|
|
259825
|
+
'-',
|
|
259826
|
+
'anonymous',
|
|
259827
|
+
'anonymoususer',
|
|
259828
|
+
]);
|
|
259829
|
+
/**
|
|
259830
|
+
* `local@domain.tld` with no whitespace, checked with a single pass rather than
|
|
259831
|
+
* a backtracking pattern (CodeQL flags `[^@]+@[^@]+\.[^@]+` as polynomial on
|
|
259832
|
+
* adversarial input).
|
|
259833
|
+
*/
|
|
259834
|
+
const isEmailLike = (value) => {
|
|
259835
|
+
const text = value.trim();
|
|
259836
|
+
if (!text || /\s/.test(text))
|
|
259837
|
+
return false;
|
|
259838
|
+
const at = text.indexOf('@');
|
|
259839
|
+
if (at <= 0 || at !== text.lastIndexOf('@') || at === text.length - 1)
|
|
259840
|
+
return false;
|
|
259841
|
+
const domain = text.slice(at + 1);
|
|
259842
|
+
const dot = domain.indexOf('.');
|
|
259843
|
+
// A dot somewhere inside the domain, with something after the last one.
|
|
259844
|
+
return dot > 0 && !domain.endsWith('.');
|
|
259845
|
+
};
|
|
259846
|
+
/** Trimmed string, or undefined for blanks, non-strings and placeholders. */
|
|
259847
|
+
const clean = (value) => {
|
|
259848
|
+
if (typeof value !== 'string')
|
|
259849
|
+
return undefined;
|
|
259850
|
+
const trimmed = value.trim();
|
|
259851
|
+
return PLACEHOLDERS.has(trimmed.toLowerCase()) ? undefined : trimmed;
|
|
259852
|
+
};
|
|
259853
|
+
const firstOf = (...candidates) => {
|
|
259854
|
+
for (const candidate of candidates) {
|
|
259855
|
+
const value = clean(candidate);
|
|
259856
|
+
if (value)
|
|
259857
|
+
return value;
|
|
259858
|
+
}
|
|
259859
|
+
return undefined;
|
|
259860
|
+
};
|
|
259861
|
+
const nested = (source) => source.user && typeof source.user === 'object' ? source.user : undefined;
|
|
259862
|
+
/**
|
|
259863
|
+
* Resolve how a user should be displayed from any payload that names one.
|
|
259864
|
+
*
|
|
259865
|
+
* - `email`: `lti_email`, `email`, `user_email`, the nested `user`'s email —
|
|
259866
|
+
* or a username field that turns out to hold an email address.
|
|
259867
|
+
* - `username`: `lti_username`, `username`, `student`, `user_username`, the
|
|
259868
|
+
* nested `user`'s username, or a bare string/number `user` value.
|
|
259869
|
+
* - `name`: `user_full_name`, `full_name`, `name`, `first_name last_name`, or
|
|
259870
|
+
* the nested `user`'s name.
|
|
259871
|
+
* - `profileUsername`: the first *platform* username (never an LTI claim).
|
|
259872
|
+
*
|
|
259873
|
+
* A string source is treated as an email when it looks like one, else as a
|
|
259874
|
+
* username. Placeholder strings ("None", "-", "anonymous"…) count as absent.
|
|
259875
|
+
*
|
|
259876
|
+
* The default order flips to name → email → username when the record carries a
|
|
259877
|
+
* real name (see {@link hasDistinctName}): "Joseph Oti" is more use to an admin
|
|
259878
|
+
* than his address, while a name that merely repeats the username is not, so
|
|
259879
|
+
* those records keep showing the email. An explicit `order` is always obeyed.
|
|
259880
|
+
*/
|
|
259881
|
+
function resolveUserIdentity(source, options = {}) {
|
|
259882
|
+
var _a, _b;
|
|
259883
|
+
const anonymousLabel = options.anonymousLabel || DEFAULT_ANONYMOUS_LABEL;
|
|
259884
|
+
const order = ((_a = options.order) === null || _a === void 0 ? void 0 : _a.length) ? options.order : DEFAULT_USER_IDENTITY_ORDER;
|
|
259885
|
+
const record = source && typeof source === 'object' ? source : { user: source !== null && source !== void 0 ? source : null };
|
|
259886
|
+
const inner = nested(record);
|
|
259887
|
+
// A bare `user` value is an id or username; numeric ids are still shown.
|
|
259888
|
+
const bareUser = typeof record.user === 'number' && Number.isFinite(record.user)
|
|
259889
|
+
? String(record.user)
|
|
259890
|
+
: typeof record.user === 'string'
|
|
259891
|
+
? clean(record.user)
|
|
259892
|
+
: undefined;
|
|
259893
|
+
let email = firstOf(record.lti_email, record.email, record.user_email, inner === null || inner === void 0 ? void 0 : inner.lti_email, inner === null || inner === void 0 ? void 0 : inner.email, inner === null || inner === void 0 ? void 0 : inner.user_email);
|
|
259894
|
+
const platformUsername = firstOf(record.username, record.student, record.user_username, inner === null || inner === void 0 ? void 0 : inner.username, inner === null || inner === void 0 ? void 0 : inner.student, inner === null || inner === void 0 ? void 0 : inner.user_username, bareUser && !isEmailLike(bareUser) ? bareUser : undefined);
|
|
259895
|
+
let username = firstOf(record.lti_username, inner === null || inner === void 0 ? void 0 : inner.lti_username, platformUsername);
|
|
259896
|
+
const composed = [clean(record.first_name), clean(record.last_name)].filter(Boolean).join(' ');
|
|
259897
|
+
const name = firstOf(record.user_full_name, record.full_name, record.name, composed, inner === null || inner === void 0 ? void 0 : inner.user_full_name, inner === null || inner === void 0 ? void 0 : inner.full_name, inner === null || inner === void 0 ? void 0 : inner.name);
|
|
259898
|
+
// A bare string that looks like an email is an email, and so is a
|
|
259899
|
+
// "username" field that actually carries one.
|
|
259900
|
+
if (!email && bareUser && isEmailLike(bareUser))
|
|
259901
|
+
email = bareUser;
|
|
259902
|
+
if (!email && username && isEmailLike(username)) {
|
|
259903
|
+
email = username;
|
|
259904
|
+
username = undefined;
|
|
259905
|
+
}
|
|
259906
|
+
const facts = { email, username, name };
|
|
259907
|
+
// Every username on the record counts: the platform one a profile opens on
|
|
259908
|
+
// and the LTI claim that would be displayed.
|
|
259909
|
+
const labelOrder = !((_b = options.order) === null || _b === void 0 ? void 0 : _b.length) && hasDistinctName(name, platformUsername, username)
|
|
259910
|
+
? NAME_FIRST_ORDER
|
|
259911
|
+
: order;
|
|
259912
|
+
const label = labelOrder.map((field) => facts[field]).find(Boolean);
|
|
259913
|
+
return {
|
|
259914
|
+
label: label !== null && label !== void 0 ? label : anonymousLabel,
|
|
259915
|
+
email,
|
|
259916
|
+
username,
|
|
259917
|
+
name,
|
|
259918
|
+
profileUsername: platformUsername,
|
|
259919
|
+
initial: (label !== null && label !== void 0 ? label : anonymousLabel).charAt(0).toUpperCase() || 'A',
|
|
259920
|
+
isAnonymous: !label,
|
|
259921
|
+
};
|
|
259922
|
+
}
|
|
259923
|
+
|
|
259776
259924
|
const asArray = (value) => (Array.isArray(value) ? value : []);
|
|
259777
259925
|
const hasEntries = (value) => !!value && typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length > 0;
|
|
259778
259926
|
/** Rollup counts as the conversation-list endpoint reports them: numbers, numeric strings or `null`. */
|
|
@@ -260084,7 +260232,7 @@ function SectionHeading({ icon, children }) {
|
|
|
260084
260232
|
return (jsxs("h4", { className: "mb-2 flex items-center gap-1.5 text-xs font-semibold tracking-wide text-gray-700 uppercase", children: [icon, children] }));
|
|
260085
260233
|
}
|
|
260086
260234
|
const MODEL_KEYS = new Set(['llm_model', 'llm_provider']);
|
|
260087
|
-
function MetadataSection({ metadata, renderModel, }) {
|
|
260235
|
+
function MetadataSection({ metadata, renderModel, tenantKey, mentorId, }) {
|
|
260088
260236
|
var _a, _b;
|
|
260089
260237
|
const t = useT();
|
|
260090
260238
|
const labelFor = useFieldLabels();
|
|
@@ -260092,7 +260240,9 @@ function MetadataSection({ metadata, renderModel, }) {
|
|
|
260092
260240
|
const model = ((_a = metadata.llm_model) !== null && _a !== void 0 ? _a : '').trim() || undefined;
|
|
260093
260241
|
const provider = ((_b = metadata.llm_provider) !== null && _b !== void 0 ? _b : '').trim() || undefined;
|
|
260094
260242
|
const hasModel = !!(model || provider);
|
|
260095
|
-
return (jsxs("section", { "data-testid": "transcript-turn-metadata", children: [jsx(SectionHeading, { icon: jsx(Info$3, { className: "h-3.5 w-3.5", "aria-hidden": "true" }), children: t('analyticsTranscriptTurnDetails.metadata') }), hasModel && (jsx("div", { "data-testid": "transcript-turn-model", className: "mb-2", children:
|
|
260243
|
+
return (jsxs("section", { "data-testid": "transcript-turn-metadata", children: [jsx(SectionHeading, { icon: jsx(Info$3, { className: "h-3.5 w-3.5", "aria-hidden": "true" }), children: t('analyticsTranscriptTurnDetails.metadata') }), hasModel && (jsx("div", { "data-testid": "transcript-turn-model", className: "mb-2", children: renderModel
|
|
260244
|
+
? renderModel({ model, provider })
|
|
260245
|
+
: defaultRenderModel({ model, provider, tenantKey, mentorId }) })), jsx(DetailRows, { entries: rest, labelFor: labelFor })] }));
|
|
260096
260246
|
}
|
|
260097
260247
|
function RequestContextSection({ requestContext }) {
|
|
260098
260248
|
const t = useT();
|
|
@@ -260107,7 +260257,7 @@ function RequestContextSection({ requestContext }) {
|
|
|
260107
260257
|
*/
|
|
260108
260258
|
const defaultRenderDocuments = (documents) => (jsx(RetrievedDocumentsButton, { documents: toRetrievedDocuments(documents) }));
|
|
260109
260259
|
const defaultRenderToolCalls = (toolCalls) => (jsx(ToolCallIndicator, { toolCalls: toToolCallInfos(toolCalls), showResults: true }));
|
|
260110
|
-
const defaultRenderModel = ({ model, provider }) =>
|
|
260260
|
+
const defaultRenderModel = ({ model, provider, tenantKey, mentorId, }) => jsx(LlmModelBadge, { model: model, provider: provider, tenantKey: tenantKey, mentorId: mentorId });
|
|
260111
260261
|
const defaultRenderFiles = (files) => (jsx(TranscriptFileCards, { files: files }));
|
|
260112
260262
|
/**
|
|
260113
260263
|
* Collapsible "Sources / Tools / Metadata" panel for one AI turn of an admin
|
|
@@ -260117,7 +260267,7 @@ const defaultRenderFiles = (files) => (jsx(TranscriptFileCards, { files: files }
|
|
|
260117
260267
|
* renders nothing at all when every field is null or empty, so it is safe to
|
|
260118
260268
|
* drop under every AI bubble.
|
|
260119
260269
|
*/
|
|
260120
|
-
function TranscriptTurnDetails({ turn, className, defaultOpen = false, renderDocuments, renderToolCalls, renderModel, renderFiles, }) {
|
|
260270
|
+
function TranscriptTurnDetails({ turn, className, defaultOpen = false, renderDocuments, renderToolCalls, renderModel, renderFiles, tenantKey, mentorId, }) {
|
|
260121
260271
|
const t = useT();
|
|
260122
260272
|
const labelFor = useFieldLabels();
|
|
260123
260273
|
const [open, setOpen] = React__default.useState(defaultOpen);
|
|
@@ -260131,7 +260281,7 @@ function TranscriptTurnDetails({ turn, className, defaultOpen = false, renderDoc
|
|
|
260131
260281
|
const requestContext = hasEntries(turn === null || turn === void 0 ? void 0 : turn.request_context) ? turn === null || turn === void 0 ? void 0 : turn.request_context : null;
|
|
260132
260282
|
return (jsx(DetailCtx.Provider, { value: detailContext, children: jsxs(Collapsible, { open: open, onOpenChange: setOpen, className: cn('mt-2', className), "data-testid": "transcript-turn-details", children: [jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [jsx(CollapsibleTrigger, { asChild: true, children: jsxs(Button$1, { type: "button", variant: "outline", size: "sm", className: "flex items-center gap-2 border-gray-200 bg-white text-gray-700 hover:bg-gray-50", "aria-expanded": open, children: [jsx(ChevronDown, { className: cn('h-4 w-4 transition-transform', open && 'rotate-180'), "aria-hidden": "true" }), open
|
|
260133
260283
|
? t('analyticsTranscriptTurnDetails.hideDetails')
|
|
260134
|
-
: t('analyticsTranscriptTurnDetails.showDetails')] }) }), documents.length > 0 && (jsx("div", { "data-testid": "transcript-turn-sources", className: "contents", children: (renderDocuments !== null && renderDocuments !== void 0 ? renderDocuments : defaultRenderDocuments)(documents) })), jsx(TranscriptRollupBadges, { documentsCount: 0, toolCallsCount: toolCalls.length })] }), jsxs(CollapsibleContent, { className: "mt-2 space-y-5 rounded-lg border border-gray-200 bg-white p-3", children: [toolCalls.length > 0 && (jsx("div", { "data-testid": "transcript-turn-tools", children: (renderToolCalls !== null && renderToolCalls !== void 0 ? renderToolCalls : defaultRenderToolCalls)(toolCalls) })), metadata && jsx(MetadataSection, { metadata: metadata, renderModel: renderModel }), requestContext && jsx(RequestContextSection, { requestContext: requestContext })] })] }) }));
|
|
260284
|
+
: t('analyticsTranscriptTurnDetails.showDetails')] }) }), documents.length > 0 && (jsx("div", { "data-testid": "transcript-turn-sources", className: "contents", children: (renderDocuments !== null && renderDocuments !== void 0 ? renderDocuments : defaultRenderDocuments)(documents) })), jsx(TranscriptRollupBadges, { documentsCount: 0, toolCallsCount: toolCalls.length })] }), jsxs(CollapsibleContent, { className: "mt-2 space-y-5 rounded-lg border border-gray-200 bg-white p-3", children: [toolCalls.length > 0 && (jsx("div", { "data-testid": "transcript-turn-tools", children: (renderToolCalls !== null && renderToolCalls !== void 0 ? renderToolCalls : defaultRenderToolCalls)(toolCalls) })), metadata && (jsx(MetadataSection, { metadata: metadata, renderModel: renderModel, tenantKey: tenantKey, mentorId: mentorId })), requestContext && jsx(RequestContextSection, { requestContext: requestContext })] })] }) }));
|
|
260135
260285
|
}
|
|
260136
260286
|
/**
|
|
260137
260287
|
* "Documents · N" / "Tools · N" chips for a conversation row, from the
|
|
@@ -260147,118 +260297,6 @@ function TranscriptRollupBadges({ documentsCount, toolCallsCount, className, })
|
|
|
260147
260297
|
return (jsxs("span", { className: cn('inline-flex flex-wrap items-center gap-1.5', className), "data-testid": "transcript-rollup-badges", children: [documents > 0 && (jsxs(Badge, { variant: "outline", className: "gap-1 border-transparent bg-blue-50 font-normal text-blue-700", "data-testid": "transcript-sources-badge", children: [jsx(FileText, { className: "h-3 w-3", "aria-hidden": "true" }), t('analyticsTranscriptTurnDetails.sourcesBadge', { count: documents })] })), tools > 0 && (jsxs(Badge, { variant: "outline", className: "gap-1 border-transparent bg-amber-50 font-normal text-amber-800", "data-testid": "transcript-tools-badge", children: [jsx(Wrench, { className: "h-3 w-3", "aria-hidden": "true" }), t('analyticsTranscriptTurnDetails.toolsBadge', { count: tools })] }))] }));
|
|
260148
260298
|
}
|
|
260149
260299
|
|
|
260150
|
-
/**
|
|
260151
|
-
* One place that decides how a user is shown.
|
|
260152
|
-
*
|
|
260153
|
-
* Backend payloads name their user in a dozen ways — `email` / `lti_email` /
|
|
260154
|
-
* `user_email`, `username` / `student` / `lti_username`, `name` /
|
|
260155
|
-
* `full_name` / `user_full_name`, or a nested `user` object — and most of
|
|
260156
|
-
* them can be empty, `null`, or a placeholder like "None". Every surface used
|
|
260157
|
-
* to pick through those on its own. `resolveUserIdentity()` takes whatever
|
|
260158
|
-
* the payload is and returns what to show, what to open a profile on, and
|
|
260159
|
-
* the avatar initial, so the rule lives here and can be tweaked once.
|
|
260160
|
-
*
|
|
260161
|
-
* Default rule: email → full name → username → the anonymous label.
|
|
260162
|
-
*/
|
|
260163
|
-
const DEFAULT_USER_IDENTITY_ORDER = ['email', 'name', 'username'];
|
|
260164
|
-
const DEFAULT_ANONYMOUS_LABEL = 'Anonymous';
|
|
260165
|
-
/** Values backends emit instead of "nothing". Compared case-insensitively. */
|
|
260166
|
-
const PLACEHOLDERS = new Set([
|
|
260167
|
-
'',
|
|
260168
|
-
'none',
|
|
260169
|
-
'null',
|
|
260170
|
-
'undefined',
|
|
260171
|
-
'n/a',
|
|
260172
|
-
'na',
|
|
260173
|
-
'-',
|
|
260174
|
-
'anonymous',
|
|
260175
|
-
'anonymoususer',
|
|
260176
|
-
]);
|
|
260177
|
-
/**
|
|
260178
|
-
* `local@domain.tld` with no whitespace, checked with a single pass rather than
|
|
260179
|
-
* a backtracking pattern (CodeQL flags `[^@]+@[^@]+\.[^@]+` as polynomial on
|
|
260180
|
-
* adversarial input).
|
|
260181
|
-
*/
|
|
260182
|
-
const isEmailLike = (value) => {
|
|
260183
|
-
const text = value.trim();
|
|
260184
|
-
if (!text || /\s/.test(text))
|
|
260185
|
-
return false;
|
|
260186
|
-
const at = text.indexOf('@');
|
|
260187
|
-
if (at <= 0 || at !== text.lastIndexOf('@') || at === text.length - 1)
|
|
260188
|
-
return false;
|
|
260189
|
-
const domain = text.slice(at + 1);
|
|
260190
|
-
const dot = domain.indexOf('.');
|
|
260191
|
-
// A dot somewhere inside the domain, with something after the last one.
|
|
260192
|
-
return dot > 0 && !domain.endsWith('.');
|
|
260193
|
-
};
|
|
260194
|
-
/** Trimmed string, or undefined for blanks, non-strings and placeholders. */
|
|
260195
|
-
const clean = (value) => {
|
|
260196
|
-
if (typeof value !== 'string')
|
|
260197
|
-
return undefined;
|
|
260198
|
-
const trimmed = value.trim();
|
|
260199
|
-
return PLACEHOLDERS.has(trimmed.toLowerCase()) ? undefined : trimmed;
|
|
260200
|
-
};
|
|
260201
|
-
const firstOf = (...candidates) => {
|
|
260202
|
-
for (const candidate of candidates) {
|
|
260203
|
-
const value = clean(candidate);
|
|
260204
|
-
if (value)
|
|
260205
|
-
return value;
|
|
260206
|
-
}
|
|
260207
|
-
return undefined;
|
|
260208
|
-
};
|
|
260209
|
-
const nested = (source) => source.user && typeof source.user === 'object' ? source.user : undefined;
|
|
260210
|
-
/**
|
|
260211
|
-
* Resolve how a user should be displayed from any payload that names one.
|
|
260212
|
-
*
|
|
260213
|
-
* - `email`: `lti_email`, `email`, `user_email`, the nested `user`'s email —
|
|
260214
|
-
* or a username field that turns out to hold an email address.
|
|
260215
|
-
* - `username`: `lti_username`, `username`, `student`, `user_username`, the
|
|
260216
|
-
* nested `user`'s username, or a bare string/number `user` value.
|
|
260217
|
-
* - `name`: `user_full_name`, `full_name`, `name`, `first_name last_name`, or
|
|
260218
|
-
* the nested `user`'s name.
|
|
260219
|
-
* - `profileUsername`: the first *platform* username (never an LTI claim).
|
|
260220
|
-
*
|
|
260221
|
-
* A string source is treated as an email when it looks like one, else as a
|
|
260222
|
-
* username. Placeholder strings ("None", "-", "anonymous"…) count as absent.
|
|
260223
|
-
*/
|
|
260224
|
-
function resolveUserIdentity(source, options = {}) {
|
|
260225
|
-
var _a;
|
|
260226
|
-
const anonymousLabel = options.anonymousLabel || DEFAULT_ANONYMOUS_LABEL;
|
|
260227
|
-
const order = ((_a = options.order) === null || _a === void 0 ? void 0 : _a.length) ? options.order : DEFAULT_USER_IDENTITY_ORDER;
|
|
260228
|
-
const record = source && typeof source === 'object' ? source : { user: source !== null && source !== void 0 ? source : null };
|
|
260229
|
-
const inner = nested(record);
|
|
260230
|
-
// A bare `user` value is an id or username; numeric ids are still shown.
|
|
260231
|
-
const bareUser = typeof record.user === 'number' && Number.isFinite(record.user)
|
|
260232
|
-
? String(record.user)
|
|
260233
|
-
: typeof record.user === 'string'
|
|
260234
|
-
? clean(record.user)
|
|
260235
|
-
: undefined;
|
|
260236
|
-
let email = firstOf(record.lti_email, record.email, record.user_email, inner === null || inner === void 0 ? void 0 : inner.lti_email, inner === null || inner === void 0 ? void 0 : inner.email, inner === null || inner === void 0 ? void 0 : inner.user_email);
|
|
260237
|
-
const platformUsername = firstOf(record.username, record.student, record.user_username, inner === null || inner === void 0 ? void 0 : inner.username, inner === null || inner === void 0 ? void 0 : inner.student, inner === null || inner === void 0 ? void 0 : inner.user_username, bareUser && !isEmailLike(bareUser) ? bareUser : undefined);
|
|
260238
|
-
let username = firstOf(record.lti_username, inner === null || inner === void 0 ? void 0 : inner.lti_username, platformUsername);
|
|
260239
|
-
const composed = [clean(record.first_name), clean(record.last_name)].filter(Boolean).join(' ');
|
|
260240
|
-
const name = firstOf(record.user_full_name, record.full_name, record.name, composed, inner === null || inner === void 0 ? void 0 : inner.user_full_name, inner === null || inner === void 0 ? void 0 : inner.full_name, inner === null || inner === void 0 ? void 0 : inner.name);
|
|
260241
|
-
// A bare string that looks like an email is an email, and so is a
|
|
260242
|
-
// "username" field that actually carries one.
|
|
260243
|
-
if (!email && bareUser && isEmailLike(bareUser))
|
|
260244
|
-
email = bareUser;
|
|
260245
|
-
if (!email && username && isEmailLike(username)) {
|
|
260246
|
-
email = username;
|
|
260247
|
-
username = undefined;
|
|
260248
|
-
}
|
|
260249
|
-
const facts = { email, username, name };
|
|
260250
|
-
const label = order.map((field) => facts[field]).find(Boolean);
|
|
260251
|
-
return {
|
|
260252
|
-
label: label !== null && label !== void 0 ? label : anonymousLabel,
|
|
260253
|
-
email,
|
|
260254
|
-
username,
|
|
260255
|
-
name,
|
|
260256
|
-
profileUsername: platformUsername,
|
|
260257
|
-
initial: (label !== null && label !== void 0 ? label : anonymousLabel).charAt(0).toUpperCase() || 'A',
|
|
260258
|
-
isAnonymous: !label,
|
|
260259
|
-
};
|
|
260260
|
-
}
|
|
260261
|
-
|
|
260262
260300
|
const AnalyticsSettingsContext = createContext({});
|
|
260263
260301
|
AnalyticsSettingsContext.Provider;
|
|
260264
260302
|
|
|
@@ -279113,7 +279151,7 @@ function AgentHistoryTab({ renderMessageContent, PaginationComponent, onExport,
|
|
|
279113
279151
|
// Use mentor summaries data for rating and tags
|
|
279114
279152
|
const averageRating = (mentorSummaries === null || mentorSummaries === void 0 ? void 0 : mentorSummaries.rating) || 0;
|
|
279115
279153
|
const topicTags = (mentorSummaries === null || mentorSummaries === void 0 ? void 0 : mentorSummaries.tags) || [];
|
|
279116
|
-
// Who the previewed conversation belongs to (
|
|
279154
|
+
// Who the previewed conversation belongs to (the shared identity rule).
|
|
279117
279155
|
const identityOptions = { anonymousLabel: labels.conversation.anonymous };
|
|
279118
279156
|
const selectedOwner = resolveUserIdentity(selectedConversation, identityOptions);
|
|
279119
279157
|
const previewOwner = resolveUserIdentity(previewConversationContent, identityOptions);
|
|
@@ -279175,11 +279213,11 @@ function AgentHistoryTab({ renderMessageContent, PaginationComponent, onExport,
|
|
|
279175
279213
|
jsx(RetrievedDocumentsButton, { documents: documents, label: labels.conversation.documentsChip(rollup.documentsCount), className: "h-6 gap-1 rounded-md border-transparent bg-blue-50 px-2 text-xs font-normal text-blue-700 hover:bg-blue-100 [&_svg]:h-3 [&_svg]:w-3" })), jsx(TranscriptRollupBadges, { documentsCount: 0, toolCallsCount: rollup.toolCallsCount })] }))] }), jsx("div", { className: "flex items-center gap-2 text-sm text-gray-600", children: jsx(ChevronRight, { className: "h-4 w-4" }) })] }, conversation.id));
|
|
279176
279214
|
}) }), totalPages > 1 && PaginationComponent && (jsx("div", { className: "my-2", children: jsx(PaginationComponent, { currentPage: currentPage, totalPages: totalPages, onPageChange: handlePageChange, disabled: isChatHistoryFetching || isChatHistoryLoading, disableNumberedButtons: isMobile }) }))] }), jsx("div", { className: "scrollbar-hide hidden max-h-[500px] flex-col overflow-y-auto rounded-lg border bg-white p-6 shadow-sm md:flex", tabIndex: 0, role: "region", "aria-label": t('tabsHistoryTab.conversationPreviewLabel'), children: selectedConversation && previewConversationContent ? (jsxs(Fragment$1, { children: [jsxs("div", { className: "mb-4", children: [jsx("h3", { className: "mb-1 text-base font-semibold text-gray-700", children: ((_m = selectedConversation.messages[0]) === null || _m === void 0 ? void 0 : _m.human) || labels.conversation.title }), jsx("span", { className: "text-sm text-gray-500", children: format$1(new Date(selectedConversation.inserted_at), "MMM dd, yyyy 'at' h:mm a") }), jsx(RetrievedDocumentsButton, { sessionId: selectedConversation.id, documents: conversationDocuments(selectedConversation.messages).length > 0
|
|
279177
279215
|
? conversationDocuments(selectedConversation.messages)
|
|
279178
|
-
: undefined, className: "mt-2" })] }), jsxs("div", { className: "space-y-6", children: [selectedConversation.messages.map((message, index) => (jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-start gap-3", children: [jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-blue-100", children: jsx("span", { className: "text-sm font-medium text-blue-600", children: selectedOwner.initial }) }), jsxs("div", { className: "flex-1", children: [jsx("div", { className: "font-medium text-gray-700", children: jsx(UserProfileLink, { tenantKey: tenantKey, username: selectedOwner.profileUsername, display: selectedOwner.label, currentSPA: currentSPA }) }), jsx("p", { className: "mt-1 text-sm whitespace-pre-line text-gray-500", children: message.human }), jsx(TranscriptFileCards, { files: attachmentCards(message.human_files) })] })] }), jsxs("div", { className: "flex items-start gap-3 border-t border-gray-200 pt-4", children: [jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-gray-100", children: jsx("span", { className: "text-sm font-medium text-gray-600", children: t('tabsHistoryTab.aiAvatarLabel') }) }), jsxs("div", { className: "flex-1", children: [jsx("div", { className: "font-medium text-gray-700", children: labels.conversation.aiAgent }), jsx("div", { className: "mt-1 text-sm text-gray-500", children: renderAiContent(message.ai) }), jsx(TranscriptFileCards, { files: attachmentCards(message.ai_files) }), jsx(TranscriptTurnDetails, { turn: message })] })] })] }, index))), (selectedConversation === null || selectedConversation === void 0 ? void 0 : selectedConversation.memory_tracked) && (jsxs("div", { className: "border-t border-gray-200 pt-4", children: [jsxs(Button$1, { variant: "outline", onClick: () => setShowConversationMemory(!showConversationMemory), className: "flex w-full items-center justify-center gap-2 border-gray-200 text-gray-700 hover:bg-gray-50", children: [jsx(Brain, { className: "h-4 w-4" }), showConversationMemory ? labels.memory.hide : labels.memory.show] }), showConversationMemory && (jsx("div", { className: "mt-4 space-y-6", children: isConversationMemoryLoading ? (jsx("div", { className: "flex items-center justify-center py-4", children: jsx(Spinner, {}) })) : ((_o = conversationMemory === null || conversationMemory === void 0 ? void 0 : conversationMemory.entries) === null || _o === void 0 ? void 0 : _o.length) ? (jsx(Fragment$1, { children: conversationMemory.entries.map((entry, index) => (jsxs("div", { children: [jsx("h4", { className: "mb-3 font-semibold text-gray-900", children: entry.key }), jsx("ul", { className: "space-y-2", children: jsx("li", { className: "flex items-start gap-2 text-sm text-gray-600", children: entry.value }) })] }, index))) })) : (jsx("div", { className: "text-sm text-gray-500", children: labels.memory.notAvailable })) }))] }))] })] })) : (jsx("div", { className: "flex h-full items-center justify-center text-gray-500", children: labels.conversation.selectPrompt })) })] }))] }) }), jsx(Dialog, { open: isConversationPreviewModalOpen, onOpenChange: setIsConversationPreviewModalOpen, children: jsxs(DialogContent, { className: "scrollbar-hide mx-auto max-h-[80vh] max-w-[90vw] overflow-x-hidden overflow-y-auto", children: [jsx(DialogHeader, { children: jsx(DialogTitle, { className: "text-lg font-semibold text-gray-900", children: previewConversationContent
|
|
279216
|
+
: undefined, className: "mt-2" })] }), jsxs("div", { className: "space-y-6", children: [selectedConversation.messages.map((message, index) => (jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-start gap-3", children: [jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-blue-100", children: jsx("span", { className: "text-sm font-medium text-blue-600", children: selectedOwner.initial }) }), jsxs("div", { className: "flex-1", children: [jsx("div", { className: "font-medium text-gray-700", children: jsx(UserProfileLink, { tenantKey: tenantKey, username: selectedOwner.profileUsername, display: selectedOwner.label, currentSPA: currentSPA }) }), jsx("p", { className: "mt-1 text-sm whitespace-pre-line text-gray-500", children: message.human }), jsx(TranscriptFileCards, { files: attachmentCards(message.human_files) })] })] }), jsxs("div", { className: "flex items-start gap-3 border-t border-gray-200 pt-4", children: [jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-gray-100", children: jsx("span", { className: "text-sm font-medium text-gray-600", children: t('tabsHistoryTab.aiAvatarLabel') }) }), jsxs("div", { className: "flex-1", children: [jsx("div", { className: "font-medium text-gray-700", children: labels.conversation.aiAgent }), jsx("div", { className: "mt-1 text-sm text-gray-500", children: renderAiContent(message.ai) }), jsx(TranscriptFileCards, { files: attachmentCards(message.ai_files) }), jsx(TranscriptTurnDetails, { turn: message, tenantKey: tenantKey, mentorId: mentorId })] })] })] }, index))), (selectedConversation === null || selectedConversation === void 0 ? void 0 : selectedConversation.memory_tracked) && (jsxs("div", { className: "border-t border-gray-200 pt-4", children: [jsxs(Button$1, { variant: "outline", onClick: () => setShowConversationMemory(!showConversationMemory), className: "flex w-full items-center justify-center gap-2 border-gray-200 text-gray-700 hover:bg-gray-50", children: [jsx(Brain, { className: "h-4 w-4" }), showConversationMemory ? labels.memory.hide : labels.memory.show] }), showConversationMemory && (jsx("div", { className: "mt-4 space-y-6", children: isConversationMemoryLoading ? (jsx("div", { className: "flex items-center justify-center py-4", children: jsx(Spinner, {}) })) : ((_o = conversationMemory === null || conversationMemory === void 0 ? void 0 : conversationMemory.entries) === null || _o === void 0 ? void 0 : _o.length) ? (jsx(Fragment$1, { children: conversationMemory.entries.map((entry, index) => (jsxs("div", { children: [jsx("h4", { className: "mb-3 font-semibold text-gray-900", children: entry.key }), jsx("ul", { className: "space-y-2", children: jsx("li", { className: "flex items-start gap-2 text-sm text-gray-600", children: entry.value }) })] }, index))) })) : (jsx("div", { className: "text-sm text-gray-500", children: labels.memory.notAvailable })) }))] }))] })] })) : (jsx("div", { className: "flex h-full items-center justify-center text-gray-500", children: labels.conversation.selectPrompt })) })] }))] }) }), jsx(Dialog, { open: isConversationPreviewModalOpen, onOpenChange: setIsConversationPreviewModalOpen, children: jsxs(DialogContent, { className: "scrollbar-hide mx-auto max-h-[80vh] max-w-[90vw] overflow-x-hidden overflow-y-auto", children: [jsx(DialogHeader, { children: jsx(DialogTitle, { className: "text-lg font-semibold text-gray-900", children: previewConversationContent
|
|
279179
279217
|
? ((_p = previewConversationContent.messages[0]) === null || _p === void 0 ? void 0 : _p.human) || labels.conversation.title
|
|
279180
279218
|
: labels.conversation.title }) }), jsx("div", { className: "mx-auto max-w-full overflow-x-hidden", children: previewConversationContent && (jsxs("div", { className: "scrollbar-hide space-y-4 overflow-y-auto", children: [jsx("div", { className: "text-sm text-gray-500", children: format$1(new Date(previewConversationContent.inserted_at), "MMM dd, yyyy 'at' h:mm a") }), jsx(RetrievedDocumentsButton, { sessionId: previewConversationContent.id, documents: conversationDocuments(previewConversationContent.messages).length > 0
|
|
279181
279219
|
? conversationDocuments(previewConversationContent.messages)
|
|
279182
|
-
: undefined }), previewConversationContent.messages.map((message, index) => (jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-start gap-3", children: [jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-blue-100", children: jsx("span", { className: "text-sm font-medium text-blue-600", children: previewOwner.initial }) }), jsxs("div", { className: "overflow-x-hidden", children: [jsx("div", { className: "truncate font-medium text-gray-900", children: jsx(UserProfileLink, { tenantKey: tenantKey, username: previewOwner.profileUsername, display: previewOwner.label, currentSPA: currentSPA }) }), jsx("p", { className: "mt-1 text-sm whitespace-pre-line text-gray-900", children: message.human }), jsx(TranscriptFileCards, { files: attachmentCards(message.human_files) })] })] }), jsxs("div", { className: "flex items-start gap-3 border-t border-gray-200 pt-4", children: [jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-gray-100", children: jsx("span", { className: "text-sm font-medium text-gray-600", children: t('tabsHistoryTab.aiAvatarLabel') }) }), jsxs("div", { className: "overflow-x-hidden", children: [jsx("div", { className: "font-medium text-gray-900", children: labels.conversation.aiAgent }), jsx("div", { className: "mt-1 text-sm text-gray-900", children: renderAiContent(message.ai) }), jsx(TranscriptFileCards, { files: attachmentCards(message.ai_files) }), jsx(TranscriptTurnDetails, { turn: message })] })] })] }, index)))] })) })] }) })] }));
|
|
279220
|
+
: undefined }), previewConversationContent.messages.map((message, index) => (jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-start gap-3", children: [jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-blue-100", children: jsx("span", { className: "text-sm font-medium text-blue-600", children: previewOwner.initial }) }), jsxs("div", { className: "overflow-x-hidden", children: [jsx("div", { className: "truncate font-medium text-gray-900", children: jsx(UserProfileLink, { tenantKey: tenantKey, username: previewOwner.profileUsername, display: previewOwner.label, currentSPA: currentSPA }) }), jsx("p", { className: "mt-1 text-sm whitespace-pre-line text-gray-900", children: message.human }), jsx(TranscriptFileCards, { files: attachmentCards(message.human_files) })] })] }), jsxs("div", { className: "flex items-start gap-3 border-t border-gray-200 pt-4", children: [jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-gray-100", children: jsx("span", { className: "text-sm font-medium text-gray-600", children: t('tabsHistoryTab.aiAvatarLabel') }) }), jsxs("div", { className: "overflow-x-hidden", children: [jsx("div", { className: "font-medium text-gray-900", children: labels.conversation.aiAgent }), jsx("div", { className: "mt-1 text-sm text-gray-900", children: renderAiContent(message.ai) }), jsx(TranscriptFileCards, { files: attachmentCards(message.ai_files) }), jsx(TranscriptTurnDetails, { turn: message, tenantKey: tenantKey, mentorId: mentorId })] })] })] }, index)))] })) })] }) })] }));
|
|
279183
279221
|
}
|
|
279184
279222
|
|
|
279185
279223
|
const ANONYMOUS_USERNAME = 'anonymous';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iblai/iblai-js",
|
|
3
|
-
"version": "2.18.
|
|
3
|
+
"version": "2.18.1",
|
|
4
4
|
"description": "Unified JavaScript SDK for IBL.ai — re-exports data-layer, web-containers, and web-utils under a single package",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -76,10 +76,10 @@
|
|
|
76
76
|
"axios": "1.13.6",
|
|
77
77
|
"dotenv": "16.6.1",
|
|
78
78
|
"winston": "3.19.0",
|
|
79
|
-
"@iblai/data-layer": "1.19.
|
|
79
|
+
"@iblai/data-layer": "1.19.1",
|
|
80
80
|
"@iblai/mcp": "1.16.0",
|
|
81
|
-
"@iblai/web-
|
|
82
|
-
"@iblai/web-
|
|
81
|
+
"@iblai/web-utils": "2.4.9",
|
|
82
|
+
"@iblai/web-containers": "1.25.1"
|
|
83
83
|
},
|
|
84
84
|
"peerDependencies": {
|
|
85
85
|
"@iblai/iblai-api": "4.166.0-ai",
|