@iblai/iblai-js 2.9.2 → 2.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2260,7 +2260,6 @@ var analyticsAnalyticsTranscriptsStats$3 = {
2260
2260
  avgCostPerConversation: "Average cost per conversation",
2261
2261
  avgRating: "Average rating",
2262
2262
  placeholderTopics: "Topics",
2263
- placeholderUsers: "Users",
2264
2263
  clearAll: "Clear All",
2265
2264
  ariaTotalConversations: "Total transcripts conversations",
2266
2265
  conversations: "conversations",
@@ -2345,6 +2344,7 @@ var analyticsStatCard$3 = {
2345
2344
  var analyticsUserFilterSelect$3 = {
2346
2345
  filterByUser: "Filter by user",
2347
2346
  you: "You",
2347
+ allUsers: "All users",
2348
2348
  searchPlaceholder: "Search users…",
2349
2349
  noUsersFound: "No users found.",
2350
2350
  loadingUsers: "Loading users…"
@@ -9281,7 +9281,6 @@ var analyticsAnalyticsTranscriptsStats$2 = {
9281
9281
  avgCostPerConversation: "Coût moyen par conversation",
9282
9282
  avgRating: "Note moyenne",
9283
9283
  placeholderTopics: "Sujets",
9284
- placeholderUsers: "Utilisateurs",
9285
9284
  clearAll: "Tout effacer",
9286
9285
  ariaTotalConversations: "Total des conversations transcrites",
9287
9286
  conversations: "conversations",
@@ -9366,6 +9365,7 @@ var analyticsStatCard$2 = {
9366
9365
  var analyticsUserFilterSelect$2 = {
9367
9366
  filterByUser: "Filtrer par utilisateur",
9368
9367
  you: "Vous",
9368
+ allUsers: "Tous les utilisateurs",
9369
9369
  searchPlaceholder: "Rechercher des utilisateurs…",
9370
9370
  noUsersFound: "Aucun utilisateur trouvé.",
9371
9371
  loadingUsers: "Chargement des utilisateurs…"
@@ -16302,7 +16302,6 @@ var analyticsAnalyticsTranscriptsStats$1 = {
16302
16302
  avgCostPerConversation: "Costo promedio por conversación",
16303
16303
  avgRating: "Calificación promedio",
16304
16304
  placeholderTopics: "Temas",
16305
- placeholderUsers: "Usuarios",
16306
16305
  clearAll: "Limpiar todo",
16307
16306
  ariaTotalConversations: "Total de conversaciones transcritas",
16308
16307
  conversations: "conversaciones",
@@ -16387,6 +16386,7 @@ var analyticsStatCard$1 = {
16387
16386
  var analyticsUserFilterSelect$1 = {
16388
16387
  filterByUser: "Filtrar por usuario",
16389
16388
  you: "Tú",
16389
+ allUsers: "Todos los usuarios",
16390
16390
  searchPlaceholder: "Buscar usuarios…",
16391
16391
  noUsersFound: "No se encontraron usuarios.",
16392
16392
  loadingUsers: "Cargando usuarios…"
@@ -23323,7 +23323,6 @@ var analyticsAnalyticsTranscriptsStats = {
23323
23323
  avgCostPerConversation: "每次对话平均费用",
23324
23324
  avgRating: "平均评分",
23325
23325
  placeholderTopics: "主题",
23326
- placeholderUsers: "用户",
23327
23326
  clearAll: "清除全部",
23328
23327
  ariaTotalConversations: "对话记录总数",
23329
23328
  conversations: "次对话",
@@ -23408,6 +23407,7 @@ var analyticsStatCard = {
23408
23407
  var analyticsUserFilterSelect = {
23409
23408
  filterByUser: "按用户筛选",
23410
23409
  you: "我",
23410
+ allUsers: "所有用户",
23411
23411
  searchPlaceholder: "搜索用户…",
23412
23412
  noUsersFound: "未找到用户。",
23413
23413
  loadingUsers: "正在加载用户…"
@@ -228367,6 +228367,115 @@ const useTranscripts = ({ tenantKey, mentorId, usergroupIds, }) => {
228367
228367
  };
228368
228368
  };
228369
228369
 
228370
+ const USERS_PAGE_SIZE = 50;
228371
+ const SEARCH_DEBOUNCE_MS = 300;
228372
+ /** cmdk needs a non-empty value; the handler treats this one as "clear". */
228373
+ const SELF_VALUE = '__self__';
228374
+ /**
228375
+ * RBAC action a watcher must hold on a user for their memories to be listable —
228376
+ * the same one the memory endpoints check, so the dropdown can't offer a user
228377
+ * whose memories would then come back 403.
228378
+ */
228379
+ const MEMORY_LIST_ACTION = 'Ibl.Mentor/Memory/list';
228380
+ function useDebouncedValue(value, delay) {
228381
+ const [debounced, setDebounced] = useState(value);
228382
+ useEffect(() => {
228383
+ const timer = setTimeout(() => setDebounced(value), delay);
228384
+ return () => clearTimeout(timer);
228385
+ }, [value, delay]);
228386
+ return debounced;
228387
+ }
228388
+ /**
228389
+ * Tenant user picker for the analytics filter rows — memory and transcripts.
228390
+ * Both endpoints behind it do the searching, so the dropdown only pages in the
228391
+ * first {@link USERS_PAGE_SIZE} matches for whatever is typed rather than
228392
+ * filtering client-side.
228393
+ *
228394
+ * Which endpoint serves the list depends on the caller:
228395
+ * - Admins get `GET /api/core/platform/users/` — every user on the tenant.
228396
+ * - Pure watchers get `GET /api/core/users/accessible/`, which the platform
228397
+ * endpoint would 403 for. It returns only the users they hold
228398
+ * {@link MEMORY_LIST_ACTION} on, i.e. the members of the groups they watch.
228399
+ */
228400
+ function UserFilterSelect({ tenantKey, currentUsername, value, onChange, showSelfOption = true, className, }) {
228401
+ const t = useT();
228402
+ const [open, setOpen] = useState(false);
228403
+ const [search, setSearch] = useState('');
228404
+ const debouncedSearch = useDebouncedValue(search, SEARCH_DEBOUNCE_MS);
228405
+ const { currentTenant } = useCurrentTenant();
228406
+ const rbacPermissions = useSelector(selectRbacPermissions);
228407
+ const isWatcher = isPureWatcher(rbacPermissions, currentTenant);
228408
+ // Only fetched once the dropdown is open — the trigger shows the selected
228409
+ // username, which is the filter value itself, so nothing needs looking up
228410
+ // first.
228411
+ const { data, isFetching: isFetchingPlatformUsers } = usePlatformUsersQuery({
228412
+ platform_key: tenantKey,
228413
+ platform_org: tenantKey,
228414
+ page: 1,
228415
+ page_size: USERS_PAGE_SIZE,
228416
+ query: debouncedSearch,
228417
+ return_policies: 'false',
228418
+ }, { skip: !open || !tenantKey || isWatcher });
228419
+ // The accessible endpoint scopes itself to the caller's platform from their
228420
+ // token, so it takes no tenant param. It also filters per field instead of
228421
+ // with one search param, hence the "@" routing the term to `email` and
228422
+ // anything else to `username`.
228423
+ const { data: accessibleUsers, isFetching: isFetchingAccessibleUsers } = useGetAccessibleUsersQuery({
228424
+ required_action: MEMORY_LIST_ACTION,
228425
+ page: 1,
228426
+ page_size: USERS_PAGE_SIZE,
228427
+ sort: 'name',
228428
+ ...(debouncedSearch.includes('@')
228429
+ ? { email: debouncedSearch }
228430
+ : { username: debouncedSearch }),
228431
+ }, { skip: !open || !isWatcher });
228432
+ const isFetching = isWatcher ? isFetchingAccessibleUsers : isFetchingPlatformUsers;
228433
+ const users = useMemo(() => {
228434
+ var _a, _b;
228435
+ let list;
228436
+ if (isWatcher) {
228437
+ list = ((_a = accessibleUsers === null || accessibleUsers === void 0 ? void 0 : accessibleUsers.results) !== null && _a !== void 0 ? _a : []).map((user) => ({
228438
+ key: String(user.id),
228439
+ username: user.username,
228440
+ name: user.name,
228441
+ email: user.email,
228442
+ }));
228443
+ }
228444
+ else {
228445
+ // `results` is a bare array unless policies were requested, in which case
228446
+ // the users sit under `results.data`.
228447
+ const results = data === null || data === void 0 ? void 0 : data.results;
228448
+ const platformUsers = Array.isArray(results)
228449
+ ? results
228450
+ : ((_b = results === null || results === void 0 ? void 0 : results.data) !== null && _b !== void 0 ? _b : []);
228451
+ list = platformUsers.map((user) => {
228452
+ var _a;
228453
+ return ({
228454
+ key: String((_a = user.user_id) !== null && _a !== void 0 ? _a : user.username),
228455
+ username: user.username,
228456
+ name: user.name,
228457
+ email: user.email,
228458
+ });
228459
+ });
228460
+ }
228461
+ return list.filter(
228462
+ // Usernames are the filter values, so a user without one is unselectable,
228463
+ // and the caller is dropped only when the "You" entry already stands for
228464
+ // them at the top of the list.
228465
+ (user) => !!user.username && !(showSelfOption && user.username === currentUsername));
228466
+ }, [isWatcher, accessibleUsers, data, currentUsername, showSelfOption]);
228467
+ const handleSelect = (username) => {
228468
+ onChange(username);
228469
+ setOpen(false);
228470
+ };
228471
+ return (jsxs(Popover, { open: open, onOpenChange: setOpen, children: [jsx(PopoverTrigger, { asChild: true, children: jsxs(Button$1, { variant: "outline", role: "combobox", "aria-expanded": open, "aria-label": t('analyticsUserFilterSelect.filterByUser'), className: cn('h-9 w-full justify-between bg-white font-normal', className), children: [jsx("span", { className: "truncate", children: value ||
228472
+ t(showSelfOption
228473
+ ? 'analyticsUserFilterSelect.you'
228474
+ : 'analyticsUserFilterSelect.allUsers') }), jsx(ChevronsUpDown, { className: "ml-2 h-4 w-4 shrink-0 opacity-50" })] }) }), jsx(PopoverContent, { "data-iblai-dialog-interaction-layer": true, className: "z-[60] w-[280px] p-0", align: "start", children: jsxs(Command, { shouldFilter: false, children: [jsx(CommandInput, { value: search, onValueChange: setSearch, placeholder: t('analyticsUserFilterSelect.searchPlaceholder') }), jsxs(CommandList, { children: [jsxs(CommandGroup, { children: [showSelfOption && (jsxs(CommandItem, { value: SELF_VALUE, onSelect: () => handleSelect(''), children: [jsx(Check, { className: cn('mr-2 h-4 w-4', value ? 'opacity-0' : 'opacity-100') }), t('analyticsUserFilterSelect.you')] })), users.map((user) => (jsxs(CommandItem, {
228475
+ // cmdk keys items by value; the search itself is server-side.
228476
+ value: user.username, onSelect: () => handleSelect(user.username), children: [jsx(Check, { className: cn('mr-2 h-4 w-4', value === user.username ? 'opacity-100' : 'opacity-0') }), jsxs("div", { className: "flex min-w-0 flex-col", children: [jsx("span", { className: "truncate font-medium", children: user.name || user.username }), jsx("span", { className: "truncate text-xs text-gray-500", children: user.email || user.username })] })] }, user.key)))] }), isFetching && (jsx("div", { className: "px-3 py-4 text-center text-sm text-gray-500", children: t('analyticsUserFilterSelect.loadingUsers') })), !isFetching && users.length === 0 && (jsx("div", { className: "px-3 py-4 text-center text-sm text-gray-500", children: t('analyticsUserFilterSelect.noUsersFound') }))] })] }) })] }));
228477
+ }
228478
+
228370
228479
  // Utility function to format relative time
228371
228480
  const formatRelativeTime = (dateString, t) => {
228372
228481
  const date = new Date(dateString);
@@ -228405,8 +228514,14 @@ const getMessageContent = (content) => {
228405
228514
  }
228406
228515
  return '';
228407
228516
  };
228517
+ /**
228518
+ * Label for whoever a transcript belongs to. The analytics payload carries an
228519
+ * email only on newer deployments, so it wins where present and the display
228520
+ * name / username stand in behind it.
228521
+ */
228522
+ const identifyUser = (...candidates) => candidates.find((candidate) => !!candidate) || '-';
228408
228523
  function AnalyticsTranscriptsStats({ tenantKey, mentorId, selectedMentorId, usergroupIds, }) {
228409
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
228524
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
228410
228525
  const t = useT();
228411
228526
  const currentMentorId = useScopedMentorId(selectedMentorId, mentorId);
228412
228527
  const [isModalOpen, setIsModalOpen] = useState(false);
@@ -228515,18 +228630,18 @@ function AnalyticsTranscriptsStats({ tenantKey, mentorId, selectedMentorId, user
228515
228630
  };
228516
228631
  // Transcript Detail Component (reusable for both desktop and mobile)
228517
228632
  const TranscriptDetail = ({ className = '' }) => {
228518
- var _a, _b, _c, _d, _e, _f;
228633
+ var _a, _b, _c, _d, _e, _f, _g, _h;
228519
228634
  return (jsx("div", { className: cn('h-full flex flex-col', className), children: !selectedTranscript ? (jsx("div", { className: "flex items-center justify-center h-full text-gray-400", children: jsxs("div", { className: "text-center", children: [jsx(ArrowLeft, { className: "h-6 w-6 mx-auto mb-2" }), jsx("p", { children: t('analyticsAnalyticsTranscriptsStats.selectConversation') })] }) })) : isLoadingTranscriptsMessagesDetailsData ? (
228520
228635
  /* Loading state */
228521
228636
  jsx("div", { className: "flex items-center justify-center h-full text-gray-400", children: jsxs("div", { className: "text-center", children: [jsx("div", { className: "animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500 mx-auto mb-3", "aria-hidden": "true" }), jsx("p", { children: t('analyticsAnalyticsTranscriptsStats.loadingTranscript') })] }) })) : isErrorTranscriptsMessagesDetailsData || !transcriptsMessagesDetailsData ? (
228522
228637
  /* Error or no data state */
228523
228638
  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') })] }) })) : (
228524
228639
  /* Data loaded successfully */
228525
- jsxs(Fragment$1, { children: [jsxs("div", { className: "mb-4 flex-shrink-0", children: [jsx("h2", { className: "text-lg font-semibold mb-2", children: t('analyticsAnalyticsTranscriptsStats.conversationTranscript') }), jsxs("div", { className: "text-sm text-gray-500", children: [jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelFullName'), ' ', ((_a = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _a === void 0 ? void 0 : _a.name) || '-'] }), jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelUsername'), ' ', ((_b = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _b === void 0 ? void 0 : _b.username) || '-'] }), jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelAgent'), ' ', ((_c = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _c === void 0 ? void 0 : _c.mentor) || '-'] }), jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelModel'), ' ', ((_d = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _d === void 0 ? void 0 : _d.model) || '-'] })] })] }), jsx("div", { className: "flex-1 overflow-y-auto space-y-4 min-h-0", children: ((_e = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.messages) === null || _e === void 0 ? void 0 : _e.length) > 0 ? ((_f = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.messages) === null || _f === void 0 ? void 0 : _f.map((message, index) => (jsxs(React__default.Fragment, { children: [(message === null || message === void 0 ? void 0 : message.human) && (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') }) }), jsx("p", { className: "text-sm", children: getMessageContent(message.human) })] }, `${index}-human`)), (message === null || message === void 0 ? void 0 : message.ai) && (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') }) }), jsx(Markdown, { children: getMessageContent(message.ai) })] }, `${index}-ai`))] }, index)))) : (jsx("div", { className: "text-center text-gray-500 py-8", children: jsx("p", { children: t('analyticsAnalyticsTranscriptsStats.noMessages') }) })) })] })) }));
228640
+ jsxs(Fragment$1, { children: [jsxs("div", { className: "mb-4 flex-shrink-0", children: [jsx("h2", { className: "text-lg font-semibold mb-2", children: t('analyticsAnalyticsTranscriptsStats.conversationTranscript') }), jsxs("div", { className: "text-sm text-gray-500", children: [jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelFullName'), ' ', identifyUser((_a = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _a === void 0 ? void 0 : _a.email, (_b = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _b === void 0 ? void 0 : _b.name)] }), jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelUsername'), ' ', identifyUser((_c = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _c === void 0 ? void 0 : _c.email, (_d = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _d === void 0 ? void 0 : _d.username)] }), jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelAgent'), ' ', ((_e = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _e === void 0 ? void 0 : _e.mentor) || '-'] }), jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelModel'), ' ', ((_f = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _f === void 0 ? void 0 : _f.model) || '-'] })] })] }), 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) => (jsxs(React__default.Fragment, { children: [(message === null || message === void 0 ? void 0 : message.human) && (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') }) }), jsx("p", { className: "text-sm", children: getMessageContent(message.human) })] }, `${index}-human`)), (message === null || message === void 0 ? void 0 : message.ai) && (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') }) }), jsx(Markdown, { children: getMessageContent(message.ai) })] }, `${index}-ai`))] }, index)))) : (jsx("div", { className: "text-center text-gray-500 py-8", children: jsx("p", { children: t('analyticsAnalyticsTranscriptsStats.noMessages') }) })) })] })) }));
228526
228641
  };
228527
228642
  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: ((_a = transcriptsConversationHeadlineData === null || transcriptsConversationHeadlineData === void 0 ? void 0 : transcriptsConversationHeadlineData.avg_messages_per_conversation) === null || _a === void 0 ? void 0 : _a.value) || 0, percentage: ((_b = transcriptsConversationHeadlineData === null || transcriptsConversationHeadlineData === void 0 ? void 0 : transcriptsConversationHeadlineData.avg_messages_per_conversation) === null || _b === void 0 ? void 0 : _b.percentage_change) ||
228528
228643
  undefined, loading: isLoadingTranscriptsConversationHeadlineData, description: t('analyticsAnalyticsTranscriptsStats.avgMessagesPerConversationDescription') }), jsx(StatCard, { title: t('analyticsAnalyticsTranscriptsStats.avgCostPerConversation'), value: Number(((_c = transcriptsConversationHeadlineData === null || transcriptsConversationHeadlineData === void 0 ? void 0 : transcriptsConversationHeadlineData.avg_cost_per_conversation) === null || _c === void 0 ? void 0 : _c.value) || 0).toFixed(2), percentage: ((_d = transcriptsConversationHeadlineData === null || transcriptsConversationHeadlineData === void 0 ? void 0 : transcriptsConversationHeadlineData.avg_cost_per_conversation) === null || _d === void 0 ? void 0 : _d.percentage_change) ||
228529
- undefined, showDollarSign: true, loading: isLoadingTranscriptsConversationHeadlineData, description: t('analyticsAnalyticsTranscriptsStats.avgCostPerConversationDescription') }), jsx(StatCard, { title: t('analyticsAnalyticsTranscriptsStats.avgRating'), value: ((_e = transcriptsConversationHeadlineData === null || transcriptsConversationHeadlineData === void 0 ? void 0 : transcriptsConversationHeadlineData.avg_rating) === null || _e === void 0 ? void 0 : _e.value) || 0, percentage: ((_f = transcriptsConversationHeadlineData === null || transcriptsConversationHeadlineData === void 0 ? void 0 : transcriptsConversationHeadlineData.avg_rating) === null || _f === void 0 ? void 0 : _f.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]" })] }), 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.placeholderUsers'), value: filters.userSearch, onChange: (e) => setFilters({ ...filters, userSearch: e.target.value }), className: "pl-10 w-[200px]" })] }), 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 ? (
228644
+ undefined, showDollarSign: true, loading: isLoadingTranscriptsConversationHeadlineData, description: t('analyticsAnalyticsTranscriptsStats.avgCostPerConversationDescription') }), jsx(StatCard, { title: t('analyticsAnalyticsTranscriptsStats.avgRating'), value: ((_e = transcriptsConversationHeadlineData === null || transcriptsConversationHeadlineData === void 0 ? void 0 : transcriptsConversationHeadlineData.avg_rating) === null || _e === void 0 ? void 0 : _e.value) || 0, percentage: ((_f = transcriptsConversationHeadlineData === null || transcriptsConversationHeadlineData === void 0 ? void 0 : transcriptsConversationHeadlineData.avg_rating) === null || _f === void 0 ? void 0 : _f.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 ? (
228530
228645
  // Loading state
228531
228646
  jsx("div", { className: "space-y-4", children: Array.from({ length: 3 }).map((_, index) => (jsxs(Card, { 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 ? (
228532
228647
  // Results
@@ -228543,7 +228658,7 @@ function AnalyticsTranscriptsStats({ tenantKey, mentorId, selectedMentorId, user
228543
228658
  ? t('analyticsAnalyticsTranscriptsStats.sentimentNegative')
228544
228659
  : transcript.sentiment === 'positive'
228545
228660
  ? t('analyticsAnalyticsTranscriptsStats.sentimentPositive')
228546
- : t('analyticsAnalyticsTranscriptsStats.sentimentNeutral'), ' ', t('analyticsAnalyticsTranscriptsStats.userSentiment')] })] }) }), jsxs("div", { className: "flex flex-wrap gap-x-6 gap-y-2 text-xs text-gray-500 mb-2", children: [jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelUser'), ' ', (transcript === null || transcript === void 0 ? void 0 : transcript.name) || (transcript === null || transcript === void 0 ? void 0 : transcript.username) || '-'] }), jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelAgent'), ' ', (transcript === null || transcript === void 0 ? void 0 : transcript.mentor) || '-'] })] }), jsxs("div", { className: "flex flex-wrap gap-x-6 gap-y-2 text-xs text-gray-500 mb-2", children: [jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelModelCard'), ' ', (transcript === null || transcript === void 0 ? void 0 : transcript.model) || '-'] }), jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelUserId'), ' ', (transcript === null || transcript === void 0 ? void 0 : transcript.username) || '-'] })] }), 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));
228661
+ : t('analyticsAnalyticsTranscriptsStats.sentimentNeutral'), ' ', t('analyticsAnalyticsTranscriptsStats.userSentiment')] })] }) }), jsxs("div", { className: "flex flex-wrap gap-x-6 gap-y-2 text-xs text-gray-500 mb-2", children: [jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelUser'), ' ', identifyUser(transcript === null || transcript === void 0 ? void 0 : transcript.email, transcript === null || transcript === void 0 ? void 0 : transcript.name, transcript === null || transcript === void 0 ? void 0 : transcript.username)] }), jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelAgent'), ' ', (transcript === null || transcript === void 0 ? void 0 : transcript.mentor) || '-'] })] }), jsxs("div", { className: "flex flex-wrap gap-x-6 gap-y-2 text-xs text-gray-500 mb-2", children: [jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelModelCard'), ' ', (transcript === null || transcript === void 0 ? void 0 : transcript.model) || '-'] }), jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelUserId'), ' ', identifyUser(transcript === null || transcript === void 0 ? void 0 : transcript.email, transcript === null || transcript === void 0 ? void 0 : transcript.username)] })] }), 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));
228547
228662
  })) : (
228548
228663
  // Empty state
228549
228664
  jsx("div", { className: "text-center py-12", children: jsx("p", { className: "text-gray-500", children: filters.topicSearch || filters.userSearch || filters.rating
@@ -228579,7 +228694,7 @@ function AnalyticsTranscriptsStats({ tenantKey, mentorId, selectedMentorId, user
228579
228694
  /* Error or no data state */
228580
228695
  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') })] }) })) : (
228581
228696
  /* Data loaded successfully */
228582
- 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.labelFullName'), ' ', ((_g = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _g === void 0 ? void 0 : _g.name) || '-'] }), jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelUsername'), ' ', ((_h = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _h === void 0 ? void 0 : _h.username) || '-'] }), jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelAgent'), ' ', ((_j = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _j === void 0 ? void 0 : _j.mentor) || '-'] }), jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelModel'), ' ', ((_k = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _k === void 0 ? void 0 : _k.model) || '-'] })] }) }), jsx("div", { className: "space-y-4 pb-4", children: ((_l = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.messages) === null || _l === void 0 ? void 0 : _l.length) > 0 ? ((_m = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.messages) === null || _m === void 0 ? void 0 : _m.map((message, index) => (jsxs(React__default.Fragment, { children: [(message === null || message === void 0 ? void 0 : message.human) && (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') }) }), jsx("p", { className: "text-sm", children: getMessageContent(message.human) })] })), (message === null || message === void 0 ? void 0 : message.ai) && (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') }) }), jsx(Markdown, { children: getMessageContent(message.ai) })] }))] }, index)))) : (jsx("div", { className: "text-center text-gray-500 py-8", children: jsx("p", { children: t('analyticsAnalyticsTranscriptsStats.noMessages') }) })) })] })) })] }) }))] }));
228697
+ 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.labelFullName'), ' ', identifyUser((_g = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _g === void 0 ? void 0 : _g.email, (_h = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _h === void 0 ? void 0 : _h.name)] }), jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelUsername'), ' ', identifyUser((_j = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _j === void 0 ? void 0 : _j.email, (_k = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _k === void 0 ? void 0 : _k.username)] }), jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelAgent'), ' ', ((_l = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _l === void 0 ? void 0 : _l.mentor) || '-'] }), jsxs("div", { children: [t('analyticsAnalyticsTranscriptsStats.labelModel'), ' ', ((_m = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.summary) === null || _m === void 0 ? void 0 : _m.model) || '-'] })] }) }), jsx("div", { className: "space-y-4 pb-4", children: ((_o = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.messages) === null || _o === void 0 ? void 0 : _o.length) > 0 ? ((_p = transcriptsMessagesDetailsData === null || transcriptsMessagesDetailsData === void 0 ? void 0 : transcriptsMessagesDetailsData.messages) === null || _p === void 0 ? void 0 : _p.map((message, index) => (jsxs(React__default.Fragment, { children: [(message === null || message === void 0 ? void 0 : message.human) && (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') }) }), jsx("p", { className: "text-sm", children: getMessageContent(message.human) })] })), (message === null || message === void 0 ? void 0 : message.ai) && (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') }) }), jsx(Markdown, { children: getMessageContent(message.ai) })] }))] }, index)))) : (jsx("div", { className: "text-center text-gray-500 py-8", children: jsx("p", { children: t('analyticsAnalyticsTranscriptsStats.noMessages') }) })) })] })) })] }) }))] }));
228583
228698
  }
228584
228699
 
228585
228700
  const phaseConfig = {
@@ -231210,111 +231325,6 @@ function MentorSelectionGrid({ selectedMentorIds, onMentorSelect, searchQuery, o
231210
231325
  })) }), jsx(IblPagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: handlePageChange, disabled: isMentorsFetching || isMentorsLoading })] })] }));
231211
231326
  }
231212
231327
 
231213
- const USERS_PAGE_SIZE = 50;
231214
- const SEARCH_DEBOUNCE_MS = 300;
231215
- /** cmdk needs a non-empty value; the handler treats this one as "clear". */
231216
- const SELF_VALUE = '__self__';
231217
- /**
231218
- * RBAC action a watcher must hold on a user for their memories to be listable —
231219
- * the same one the memory endpoints check, so the dropdown can't offer a user
231220
- * whose memories would then come back 403.
231221
- */
231222
- const MEMORY_LIST_ACTION = 'Ibl.Mentor/Memory/list';
231223
- function useDebouncedValue(value, delay) {
231224
- const [debounced, setDebounced] = useState(value);
231225
- useEffect(() => {
231226
- const timer = setTimeout(() => setDebounced(value), delay);
231227
- return () => clearTimeout(timer);
231228
- }, [value, delay]);
231229
- return debounced;
231230
- }
231231
- /**
231232
- * Tenant user picker for the analytics memory filters. Both endpoints behind it
231233
- * do the searching, so the dropdown only pages in the first
231234
- * {@link USERS_PAGE_SIZE} matches for whatever is typed rather than filtering
231235
- * client-side.
231236
- *
231237
- * Which endpoint serves the list depends on the caller:
231238
- * - Admins get `GET /api/core/platform/users/` — every user on the tenant.
231239
- * - Pure watchers get `GET /api/core/users/accessible/`, which the platform
231240
- * endpoint would 403 for. It returns only the users they hold
231241
- * {@link MEMORY_LIST_ACTION} on, i.e. the members of the groups they watch.
231242
- */
231243
- function UserFilterSelect({ tenantKey, currentUsername, value, onChange, }) {
231244
- const t = useT();
231245
- const [open, setOpen] = useState(false);
231246
- const [search, setSearch] = useState('');
231247
- const debouncedSearch = useDebouncedValue(search, SEARCH_DEBOUNCE_MS);
231248
- const { currentTenant } = useCurrentTenant();
231249
- const rbacPermissions = useSelector(selectRbacPermissions);
231250
- const isWatcher = isPureWatcher(rbacPermissions, currentTenant);
231251
- // Only fetched once the dropdown is open — the trigger shows the selected
231252
- // username, which is the filter value itself, so nothing needs looking up
231253
- // first.
231254
- const { data, isFetching: isFetchingPlatformUsers } = usePlatformUsersQuery({
231255
- platform_key: tenantKey,
231256
- platform_org: tenantKey,
231257
- page: 1,
231258
- page_size: USERS_PAGE_SIZE,
231259
- query: debouncedSearch,
231260
- return_policies: 'false',
231261
- }, { skip: !open || !tenantKey || isWatcher });
231262
- // The accessible endpoint scopes itself to the caller's platform from their
231263
- // token, so it takes no tenant param. It also filters per field instead of
231264
- // with one search param, hence the "@" routing the term to `email` and
231265
- // anything else to `username`.
231266
- const { data: accessibleUsers, isFetching: isFetchingAccessibleUsers } = useGetAccessibleUsersQuery({
231267
- required_action: MEMORY_LIST_ACTION,
231268
- page: 1,
231269
- page_size: USERS_PAGE_SIZE,
231270
- sort: 'name',
231271
- ...(debouncedSearch.includes('@')
231272
- ? { email: debouncedSearch }
231273
- : { username: debouncedSearch }),
231274
- }, { skip: !open || !isWatcher });
231275
- const isFetching = isWatcher ? isFetchingAccessibleUsers : isFetchingPlatformUsers;
231276
- const users = useMemo(() => {
231277
- var _a, _b;
231278
- let list;
231279
- if (isWatcher) {
231280
- list = ((_a = accessibleUsers === null || accessibleUsers === void 0 ? void 0 : accessibleUsers.results) !== null && _a !== void 0 ? _a : []).map((user) => ({
231281
- key: String(user.id),
231282
- username: user.username,
231283
- name: user.name,
231284
- email: user.email,
231285
- }));
231286
- }
231287
- else {
231288
- // `results` is a bare array unless policies were requested, in which case
231289
- // the users sit under `results.data`.
231290
- const results = data === null || data === void 0 ? void 0 : data.results;
231291
- const platformUsers = Array.isArray(results)
231292
- ? results
231293
- : ((_b = results === null || results === void 0 ? void 0 : results.data) !== null && _b !== void 0 ? _b : []);
231294
- list = platformUsers.map((user) => {
231295
- var _a;
231296
- return ({
231297
- key: String((_a = user.user_id) !== null && _a !== void 0 ? _a : user.username),
231298
- username: user.username,
231299
- name: user.name,
231300
- email: user.email,
231301
- });
231302
- });
231303
- }
231304
- return list.filter(
231305
- // Usernames are the filter values, so a user without one is unselectable,
231306
- // and the caller already has their own entry at the top of the list.
231307
- (user) => !!user.username && user.username !== currentUsername);
231308
- }, [isWatcher, accessibleUsers, data, currentUsername]);
231309
- const handleSelect = (username) => {
231310
- onChange(username);
231311
- setOpen(false);
231312
- };
231313
- return (jsxs(Popover, { open: open, onOpenChange: setOpen, children: [jsx(PopoverTrigger, { asChild: true, children: jsxs(Button$1, { variant: "outline", role: "combobox", "aria-expanded": open, "aria-label": t('analyticsUserFilterSelect.filterByUser'), className: "h-9 w-full justify-between bg-white font-normal", children: [jsx("span", { className: "truncate", children: value || t('analyticsUserFilterSelect.you') }), jsx(ChevronsUpDown, { className: "ml-2 h-4 w-4 shrink-0 opacity-50" })] }) }), jsx(PopoverContent, { "data-iblai-dialog-interaction-layer": true, className: "z-[60] w-[280px] p-0", align: "start", children: jsxs(Command, { shouldFilter: false, children: [jsx(CommandInput, { value: search, onValueChange: setSearch, placeholder: t('analyticsUserFilterSelect.searchPlaceholder') }), jsxs(CommandList, { children: [jsxs(CommandGroup, { children: [jsxs(CommandItem, { value: SELF_VALUE, onSelect: () => handleSelect(''), children: [jsx(Check, { className: cn('mr-2 h-4 w-4', value ? 'opacity-0' : 'opacity-100') }), t('analyticsUserFilterSelect.you')] }), users.map((user) => (jsxs(CommandItem, {
231314
- // cmdk keys items by value; the search itself is server-side.
231315
- value: user.username, onSelect: () => handleSelect(user.username), children: [jsx(Check, { className: cn('mr-2 h-4 w-4', value === user.username ? 'opacity-100' : 'opacity-0') }), jsxs("div", { className: "flex min-w-0 flex-col", children: [jsx("span", { className: "truncate font-medium", children: user.name || user.username }), jsx("span", { className: "truncate text-xs text-gray-500", children: user.email || user.username })] })] }, user.key)))] }), isFetching && (jsx("div", { className: "px-3 py-4 text-center text-sm text-gray-500", children: t('analyticsUserFilterSelect.loadingUsers') })), !isFetching && users.length === 0 && (jsx("div", { className: "px-3 py-4 text-center text-sm text-gray-500", children: t('analyticsUserFilterSelect.noUsersFound') }))] })] }) })] }));
231316
- }
231317
-
231318
231328
  /**
231319
231329
  * The global and cross-mentor endpoints both cap `page_size` at 25 server-side,
231320
231330
  * so we ask for a size every scope can honor rather than paginating
@@ -97047,7 +97047,6 @@ var analyticsAnalyticsTranscriptsStats$3 = {
97047
97047
  avgCostPerConversation: "Average cost per conversation",
97048
97048
  avgRating: "Average rating",
97049
97049
  placeholderTopics: "Topics",
97050
- placeholderUsers: "Users",
97051
97050
  clearAll: "Clear All",
97052
97051
  ariaTotalConversations: "Total transcripts conversations",
97053
97052
  conversations: "conversations",
@@ -97132,6 +97131,7 @@ var analyticsStatCard$3 = {
97132
97131
  var analyticsUserFilterSelect$3 = {
97133
97132
  filterByUser: "Filter by user",
97134
97133
  you: "You",
97134
+ allUsers: "All users",
97135
97135
  searchPlaceholder: "Search users…",
97136
97136
  noUsersFound: "No users found.",
97137
97137
  loadingUsers: "Loading users…"
@@ -104068,7 +104068,6 @@ var analyticsAnalyticsTranscriptsStats$2 = {
104068
104068
  avgCostPerConversation: "Coût moyen par conversation",
104069
104069
  avgRating: "Note moyenne",
104070
104070
  placeholderTopics: "Sujets",
104071
- placeholderUsers: "Utilisateurs",
104072
104071
  clearAll: "Tout effacer",
104073
104072
  ariaTotalConversations: "Total des conversations transcrites",
104074
104073
  conversations: "conversations",
@@ -104153,6 +104152,7 @@ var analyticsStatCard$2 = {
104153
104152
  var analyticsUserFilterSelect$2 = {
104154
104153
  filterByUser: "Filtrer par utilisateur",
104155
104154
  you: "Vous",
104155
+ allUsers: "Tous les utilisateurs",
104156
104156
  searchPlaceholder: "Rechercher des utilisateurs…",
104157
104157
  noUsersFound: "Aucun utilisateur trouvé.",
104158
104158
  loadingUsers: "Chargement des utilisateurs…"
@@ -111089,7 +111089,6 @@ var analyticsAnalyticsTranscriptsStats$1 = {
111089
111089
  avgCostPerConversation: "Costo promedio por conversación",
111090
111090
  avgRating: "Calificación promedio",
111091
111091
  placeholderTopics: "Temas",
111092
- placeholderUsers: "Usuarios",
111093
111092
  clearAll: "Limpiar todo",
111094
111093
  ariaTotalConversations: "Total de conversaciones transcritas",
111095
111094
  conversations: "conversaciones",
@@ -111174,6 +111173,7 @@ var analyticsStatCard$1 = {
111174
111173
  var analyticsUserFilterSelect$1 = {
111175
111174
  filterByUser: "Filtrar por usuario",
111176
111175
  you: "Tú",
111176
+ allUsers: "Todos los usuarios",
111177
111177
  searchPlaceholder: "Buscar usuarios…",
111178
111178
  noUsersFound: "No se encontraron usuarios.",
111179
111179
  loadingUsers: "Cargando usuarios…"
@@ -118110,7 +118110,6 @@ var analyticsAnalyticsTranscriptsStats = {
118110
118110
  avgCostPerConversation: "每次对话平均费用",
118111
118111
  avgRating: "平均评分",
118112
118112
  placeholderTopics: "主题",
118113
- placeholderUsers: "用户",
118114
118113
  clearAll: "清除全部",
118115
118114
  ariaTotalConversations: "对话记录总数",
118116
118115
  conversations: "次对话",
@@ -118195,6 +118194,7 @@ var analyticsStatCard = {
118195
118194
  var analyticsUserFilterSelect = {
118196
118195
  filterByUser: "按用户筛选",
118197
118196
  you: "我",
118197
+ allUsers: "所有用户",
118198
118198
  searchPlaceholder: "搜索用户…",
118199
118199
  noUsersFound: "未找到用户。",
118200
118200
  loadingUsers: "正在加载用户…"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iblai/iblai-js",
3
- "version": "2.9.2",
3
+ "version": "2.9.4",
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.13.1",
80
- "@iblai/web-containers": "1.19.8",
81
- "@iblai/mcp": "1.12.6",
82
- "@iblai/web-utils": "2.4.0"
79
+ "@iblai/data-layer": "1.13.2",
80
+ "@iblai/mcp": "1.12.8",
81
+ "@iblai/web-containers": "1.19.9",
82
+ "@iblai/web-utils": "2.4.1"
83
83
  },
84
84
  "peerDependencies": {
85
85
  "@iblai/iblai-api": "4.166.0-ai",