@iblai/iblai-js 2.10.2 → 2.11.0

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.
@@ -185577,7 +185577,7 @@ const STATE_BADGE_CLASSES = {
185577
185577
  expired: 'bg-gray-50 text-gray-600',
185578
185578
  };
185579
185579
  const ACTIVE_BADGE_CLASSES = 'bg-blue-50 text-blue-700';
185580
- const timeAgo$2 = (value) => {
185580
+ const timeAgo$1 = (value) => {
185581
185581
  if (!value)
185582
185582
  return '';
185583
185583
  const date = new Date(value);
@@ -185627,7 +185627,7 @@ function ReportHistory({ org, username, onDownload }) {
185627
185627
  const state = (_a = report.state) !== null && _a !== void 0 ? _a : '';
185628
185628
  const active = isActiveState$1(state);
185629
185629
  const labelKey = (_b = STATE_LABEL_KEYS[state]) !== null && _b !== void 0 ? _b : 'statePending';
185630
- return (jsxs(TableRow, { className: "border-b border-neutral-200", children: [jsx(TableCell, { className: "p-3 whitespace-nowrap", children: jsxs(Badge, { variant: "outline", className: cn(active ? ACTIVE_BADGE_CLASSES : (_c = STATE_BADGE_CLASSES[state]) !== null && _c !== void 0 ? _c : ''), children: [active && (jsx(LoaderCircle, { className: "mr-1 h-3 w-3 animate-spin", "aria-hidden": "true" })), t(`chatHistoryReportHistory.${labelKey}`)] }) }), jsxs(TableCell, { className: "p-3 whitespace-nowrap text-[#646464]", children: [timeAgo$2(report.created_at), report.owner && report.owner !== username && (jsx("div", { className: "text-xs text-gray-500", children: t('chatHistoryReportHistory.createdBy', { owner: report.owner }) }))] }), jsx(TableCell, { className: "max-w-[280px] truncate p-3 text-[#646464]", children: filterSummary(report, t) }), jsx(TableCell, { className: "p-3 whitespace-nowrap text-[#646464]", children: state === 'completed' && report.expires ? timeAgo$2(report.expires) : '—' }), jsx(TableCell, { className: "p-3 text-right whitespace-nowrap", children: state === 'completed' && report.url && (jsxs(Button$1, { variant: "outline", size: "sm", className: "flex items-center gap-2 bg-transparent", onClick: () => handleDownload(report), disabled: downloadingId === report.report_id, children: [downloadingId === report.report_id ? (jsx(LoaderCircle, { className: "h-4 w-4 animate-spin", "aria-hidden": "true" })) : (jsx(Download, { className: "h-4 w-4", "aria-hidden": "true" })), t('chatHistoryReportHistory.download')] })) })] }, (_d = report.report_id) !== null && _d !== void 0 ? _d : report.created_at));
185630
+ return (jsxs(TableRow, { className: "border-b border-neutral-200", children: [jsx(TableCell, { className: "p-3 whitespace-nowrap", children: jsxs(Badge, { variant: "outline", className: cn(active ? ACTIVE_BADGE_CLASSES : (_c = STATE_BADGE_CLASSES[state]) !== null && _c !== void 0 ? _c : ''), children: [active && (jsx(LoaderCircle, { className: "mr-1 h-3 w-3 animate-spin", "aria-hidden": "true" })), t(`chatHistoryReportHistory.${labelKey}`)] }) }), jsxs(TableCell, { className: "p-3 whitespace-nowrap text-[#646464]", children: [timeAgo$1(report.created_at), report.owner && report.owner !== username && (jsx("div", { className: "text-xs text-gray-500", children: t('chatHistoryReportHistory.createdBy', { owner: report.owner }) }))] }), jsx(TableCell, { className: "max-w-[280px] truncate p-3 text-[#646464]", children: filterSummary(report, t) }), jsx(TableCell, { className: "p-3 whitespace-nowrap text-[#646464]", children: state === 'completed' && report.expires ? timeAgo$1(report.expires) : '—' }), jsx(TableCell, { className: "p-3 text-right whitespace-nowrap", children: state === 'completed' && report.url && (jsxs(Button$1, { variant: "outline", size: "sm", className: "flex items-center gap-2 bg-transparent", onClick: () => handleDownload(report), disabled: downloadingId === report.report_id, children: [downloadingId === report.report_id ? (jsx(LoaderCircle, { className: "h-4 w-4 animate-spin", "aria-hidden": "true" })) : (jsx(Download, { className: "h-4 w-4", "aria-hidden": "true" })), t('chatHistoryReportHistory.download')] })) })] }, (_d = report.report_id) !== null && _d !== void 0 ? _d : report.created_at));
185631
185631
  })) })] })) }) }) }));
185632
185632
  }
185633
185633
 
@@ -185842,11 +185842,17 @@ function stripMarkdown(text) {
185842
185842
  .replace(/\s+/g, ' ')
185843
185843
  .trim();
185844
185844
  }
185845
- const timeAgo$1 = (value) => {
185845
+ /**
185846
+ * When a conversation happened: the calendar date and time, followed by the
185847
+ * relative "x ago" so both the exact day and recency read at a glance.
185848
+ */
185849
+ const formatSessionDate = (value) => {
185846
185850
  if (!value)
185847
185851
  return '';
185848
185852
  const date = new Date(value);
185849
- return Number.isNaN(date.getTime()) ? '' : formatDistanceToNow(date, { addSuffix: true });
185853
+ if (Number.isNaN(date.getTime()))
185854
+ return '';
185855
+ return `${format$1(date, 'MMM d, yyyy, h:mm a')} · ${formatDistanceToNow(date, { addSuffix: true })}`;
185850
185856
  };
185851
185857
  /**
185852
185858
  * The list endpoint may serialize `messages` as a JSON string while the detail
@@ -185947,11 +185953,18 @@ function AgentMessageAvatar({ agent, fallback }) {
185947
185953
  return (jsxs(Avatar, { className: "h-8 w-8 flex-shrink-0", children: [jsx(AvatarImage, { src: agent.profileImage, alt: "" }), jsx(AvatarFallback, { className: "bg-gray-100 text-sm font-medium text-gray-600", children: ((_b = (_a = agent.name) === null || _a === void 0 ? void 0 : _a.charAt(0)) === null || _b === void 0 ? void 0 : _b.toUpperCase()) || fallback })] }));
185948
185954
  }
185949
185955
  /** Messages of one conversation, in the agent-settings History tab's style. */
185950
- function ConversationMessages({ messages, username, agent, }) {
185956
+ function ConversationMessages({ messages, username, agent, fallbackEmail, }) {
185951
185957
  const t = useT();
185958
+ // On your own profile the human turns are "You"; an admin or watcher
185959
+ // reading someone else's history sees that user's email instead.
185960
+ const isPreview = useIsProfilePreview(username);
185961
+ const { data: userMetadata } = useGetUserMetadataQuery({ params: { username } });
185962
+ const humanLabel = isPreview
185963
+ ? (userMetadata === null || userMetadata === void 0 ? void 0 : userMetadata.email) || fallbackEmail || username
185964
+ : t('chatHistoryChatHistoryTab.you');
185952
185965
  return (jsx("div", { className: "space-y-6", children: messages.map((message, index) => {
185953
185966
  var _a, _b;
185954
- return (jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-start gap-3", children: [jsx(UserMessageAvatar, { username: username }), jsxs("div", { className: "min-w-0 flex-1 overflow-x-hidden", children: [jsx("div", { className: "font-medium text-gray-700", children: t('chatHistoryChatHistoryTab.you') }), jsx(Markdown, { className: "mt-1 text-sm text-gray-500", children: (_a = message.human) !== null && _a !== void 0 ? _a : '' })] })] }), jsxs("div", { className: "flex items-start gap-3 border-t border-gray-200 pt-4", children: [jsx(AgentMessageAvatar, { agent: agent, fallback: t('chatHistoryChatHistoryTab.aiAvatarLabel') }), jsxs("div", { className: "min-w-0 flex-1 overflow-x-hidden", children: [jsx("div", { className: "font-medium text-gray-700", children: agent.name }), jsx(Markdown, { className: "mt-1 text-sm text-gray-500", children: (_b = message.ai) !== null && _b !== void 0 ? _b : '' })] })] })] }, index));
185967
+ return (jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-start gap-3", children: [jsx(UserMessageAvatar, { username: username }), jsxs("div", { className: "min-w-0 flex-1 overflow-x-hidden", children: [jsx("div", { className: "font-medium break-all text-gray-700", children: humanLabel }), jsx(Markdown, { className: "mt-1 text-sm text-gray-500", children: (_a = message.human) !== null && _a !== void 0 ? _a : '' })] })] }), jsxs("div", { className: "flex items-start gap-3 border-t border-gray-200 pt-4", children: [jsx(AgentMessageAvatar, { agent: agent, fallback: t('chatHistoryChatHistoryTab.aiAvatarLabel') }), jsxs("div", { className: "min-w-0 flex-1 overflow-x-hidden", children: [jsx("div", { className: "font-medium text-gray-700", children: agent.name }), jsx(Markdown, { className: "mt-1 text-sm text-gray-500", children: (_b = message.ai) !== null && _b !== void 0 ? _b : '' })] })] })] }, index));
185955
185968
  }) }));
185956
185969
  }
185957
185970
  /**
@@ -185969,7 +185982,7 @@ function ConversationPreview({ org, username, session, agent, }) {
185969
185982
  if (isLoading) {
185970
185983
  return (jsx("div", { className: "flex items-center justify-center py-10", children: jsx(Spinner, {}) }));
185971
185984
  }
185972
- return (jsxs(Fragment$1, { children: [jsxs("div", { className: "mb-4 flex items-start justify-between gap-3", children: [jsxs("div", { className: "min-w-0", children: [jsx("h3", { className: "mb-1 text-base font-semibold text-gray-700", children: textTruncate(title, 80) }), jsx("span", { className: "text-sm text-gray-500", children: timeAgo$1(session.inserted_at) })] }), messages.length > 0 && (jsxs(Button$1, { variant: "outline", size: "sm", className: "flex flex-shrink-0 items-center gap-2 bg-transparent", onClick: () => downloadTextFile(`conversation-${session.id}.csv`, conversationToCsv(session, messages, agent.name), 'text/csv'), children: [jsx(Download, { className: "h-4 w-4" }), t('chatHistoryChatHistoryTab.download')] }))] }), messages.length > 0 ? (jsx(ConversationMessages, { messages: messages, username: username, agent: agent })) : (jsx("p", { className: "text-sm text-gray-500", children: t('chatHistoryChatHistoryTab.noMessages') }))] }));
185985
+ return (jsxs(Fragment$1, { children: [jsxs("div", { className: "mb-4 flex items-start justify-between gap-3", children: [jsxs("div", { className: "min-w-0", children: [jsx("h3", { className: "mb-1 text-base font-semibold text-gray-700", children: textTruncate(title, 80) }), jsx("span", { className: "text-sm text-gray-500", children: formatSessionDate(session.inserted_at) })] }), messages.length > 0 && (jsxs(Button$1, { variant: "outline", size: "sm", className: "flex flex-shrink-0 items-center gap-2 bg-transparent", onClick: () => downloadTextFile(`conversation-${session.id}.csv`, conversationToCsv(session, messages, agent.name), 'text/csv'), children: [jsx(Download, { className: "h-4 w-4" }), t('chatHistoryChatHistoryTab.download')] }))] }), messages.length > 0 ? (jsx(ConversationMessages, { messages: messages, username: username, agent: agent, fallbackEmail: session.email })) : (jsx("p", { className: "text-sm text-gray-500", children: t('chatHistoryChatHistoryTab.noMessages') }))] }));
185973
185986
  }
185974
185987
  /**
185975
185988
  * Profile-level view of the user's own conversations across every agent —
@@ -186050,7 +186063,7 @@ function ChatHistoryTab({ org, username }) {
186050
186063
  event.preventDefault();
186051
186064
  handleConversationClick(session);
186052
186065
  }
186053
- }, children: [jsxs("div", { className: "w-full", children: [jsxs("div", { className: "mb-2 flex items-center justify-between gap-2", children: [jsxs("span", { className: "flex items-center gap-2 text-sm text-gray-600", children: [timeAgo$1(session.inserted_at), sentiment && (jsx(Badge, { variant: "outline", className: cn('font-normal', (_d = SENTIMENT_CLASSES[sentiment]) !== null && _d !== void 0 ? _d : SENTIMENT_CLASSES.neutral), children: sentimentLabel((_e = session.sentiment) !== null && _e !== void 0 ? _e : '') }))] }), jsx("span", { className: "max-w-[100px] truncate text-sm text-gray-900 sm:max-w-[200px]", children: resolveAgent(session).name })] }), jsx("div", { className: "font-medium text-gray-900", children: textTruncate(title, 50) }), preview && (jsx("p", { className: "line-clamp-1 text-sm text-gray-600", children: textTruncate(preview, 60) }))] }), jsx("div", { className: "flex items-center gap-2 text-sm text-gray-600", children: jsx(ChevronRight, { className: "h-4 w-4" }) })] }, session.id));
186066
+ }, children: [jsxs("div", { className: "w-full", children: [jsxs("div", { className: "mb-2 flex items-center justify-between gap-2", children: [jsxs("span", { className: "flex items-center gap-2 text-sm text-gray-600", children: [formatSessionDate(session.inserted_at), sentiment && (jsx(Badge, { variant: "outline", className: cn('font-normal', (_d = SENTIMENT_CLASSES[sentiment]) !== null && _d !== void 0 ? _d : SENTIMENT_CLASSES.neutral), children: sentimentLabel((_e = session.sentiment) !== null && _e !== void 0 ? _e : '') }))] }), jsx("span", { className: "max-w-[100px] truncate text-sm text-gray-900 sm:max-w-[200px]", children: resolveAgent(session).name })] }), jsx("div", { className: "font-medium text-gray-900", children: textTruncate(title, 50) }), preview && (jsx("p", { className: "line-clamp-1 text-sm text-gray-600", children: textTruncate(preview, 60) }))] }), jsx("div", { className: "flex items-center gap-2 text-sm text-gray-600", children: jsx(ChevronRight, { className: "h-4 w-4" }) })] }, session.id));
186054
186067
  }) }), totalPages > 1 && (jsx("div", { className: "flex-shrink-0", children: jsx(IblPagination, { currentPage: page, totalPages: totalPages, onPageChange: setPage, disabled: isFetching || isLoading, disableNumberedButtons: isMobile }) }))] }), jsx("div", { className: "scrollbar-hide hidden min-h-0 flex-col overflow-y-auto rounded-lg border border-neutral-200 bg-white p-6 shadow-sm md:flex", tabIndex: 0, role: "region", "aria-label": t('chatHistoryChatHistoryTab.conversationPreviewLabel'), children: selectedSession ? (jsx(ConversationPreview, { org: org, username: username, session: selectedSession, agent: resolveAgent(selectedSession) })) : (jsx("div", { className: "flex h-full items-center justify-center text-gray-500", children: t('chatHistoryChatHistoryTab.selectPrompt') })) })] })), jsx(Dialog, { open: isPreviewModalOpen, onOpenChange: setIsPreviewModalOpen, 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: selectedSession
186055
186068
  ? stripMarkdown(selectedSession.title || t('chatHistoryChatHistoryTab.conversation'))
186056
186069
  : t('chatHistoryChatHistoryTab.conversation') }) }), jsx("div", { className: "mx-auto max-w-full overflow-x-hidden", children: selectedSession && (jsx(ConversationPreview, { org: org, username: username, session: selectedSession, agent: resolveAgent(selectedSession) })) })] }) })] }), jsx(TabsContent, { value: "exports", className: "mt-0 min-h-0 flex-1 overflow-y-auto data-[state=inactive]:hidden", children: jsx(ReportHistory, { org: org, username: username, onDownload: downloadFromUrl }) })] }));
@@ -190944,15 +190957,17 @@ const PAGE_SIZE$7 = 20;
190944
190957
  const SEARCH_DEBOUNCE_MS$3 = 300;
190945
190958
  /**
190946
190959
  * The public application URL on the auth SPA for one form of a platform
190947
- * (`<auth-url>/applications/<platform-key>/<form-id>?redirect-to=<origin>`), or
190948
- * null when the auth SPA URL cannot be resolved. `authSpaUrl` (prop) wins;
190960
+ * (`<auth-url>/applications/<platform-key>/<form-id>?redirect-to=<origin>&app=<spa>`),
190961
+ * or null when the auth SPA URL cannot be resolved. `authSpaUrl` (prop) wins;
190949
190962
  * otherwise the runtime-injected env (`window.__ENV__.NEXT_PUBLIC_AUTH_URL`) is
190950
190963
  * used. With no `formId` the link resolves the platform's default form (404
190951
190964
  * when none). The `redirect-to` param carries the origin the applicant is sent
190952
190965
  * back to after submitting — by default the origin the link is copied from — and
190953
- * is omitted when no origin can be read (no DOM).
190966
+ * is omitted when no origin can be read (no DOM). The `app` param carries the
190967
+ * SPA the link was copied from, so the login flow the applicant is sent through
190968
+ * (unauthenticated visitors) is branded for that SPA; omitted when unknown.
190954
190969
  */
190955
- function buildApplicationLink(platformKey, formId, authSpaUrl, redirectTo) {
190970
+ function buildApplicationLink(platformKey, formId, authSpaUrl, redirectTo, currentSpa) {
190956
190971
  var _a, _b;
190957
190972
  const base = (_b = authSpaUrl !== null && authSpaUrl !== void 0 ? authSpaUrl : (typeof window !== 'undefined'
190958
190973
  ? (_a = window.__ENV__) === null || _a === void 0 ? void 0 : _a.NEXT_PUBLIC_AUTH_URL
@@ -190962,7 +190977,13 @@ function buildApplicationLink(platformKey, formId, authSpaUrl, redirectTo) {
190962
190977
  const root = `${trimTrailingSlashes(base)}/applications/${encodeURIComponent(platformKey)}`;
190963
190978
  const path = formId != null ? `${root}/${encodeURIComponent(String(formId))}` : root;
190964
190979
  const target = (typeof window !== 'undefined' ? window.location.origin : undefined);
190965
- return target ? `${path}?redirect-to=${encodeURIComponent(target)}` : path;
190980
+ const query = new URLSearchParams();
190981
+ if (target)
190982
+ query.set('redirect-to', target);
190983
+ if (currentSpa)
190984
+ query.set('app', currentSpa);
190985
+ const search = query.toString();
190986
+ return search ? `${path}?${search}` : path;
190966
190987
  }
190967
190988
  /**
190968
190989
  * Applications management. Entry view lists the platform's application forms
@@ -190970,7 +190991,7 @@ function buildApplicationLink(platformKey, formId, authSpaUrl, redirectTo) {
190970
190991
  * per-form (or all-form) submission pipelines, the submission workspace with
190971
190992
  * per-student decisions, the form/question editor, and the ban list.
190972
190993
  */
190973
- function ApplicationsTab({ platformKey, authSpaUrl, }) {
190994
+ function ApplicationsTab({ platformKey, authSpaUrl, currentSpa, }) {
190974
190995
  const [view, setView] = useState({ name: 'forms' });
190975
190996
  if (view.name === 'editor') {
190976
190997
  return (jsx(QuestionsEditor, { platformKey: platformKey, initialFormId: view.formId, startNew: view.startNew, onBack: () => setView({ name: 'forms' }) }));
@@ -190984,10 +191005,10 @@ function ApplicationsTab({ platformKey, authSpaUrl, }) {
190984
191005
  if (view.name === 'submissions') {
190985
191006
  return (jsx(SubmissionsPipeline, { platformKey: platformKey, form: view.form, onBack: () => setView({ name: 'forms' }), onOpen: (submissionId) => setView({ name: 'detail', submissionId, form: view.form }) }));
190986
191007
  }
190987
- return (jsx(FormsList, { platformKey: platformKey, authSpaUrl: authSpaUrl, onViewSubmissions: (form) => setView({ name: 'submissions', form }), onEditForm: (formId) => setView({ name: 'editor', formId, startNew: false }), onNewForm: () => setView({ name: 'editor', formId: null, startNew: true }), onViewBlocks: () => setView({ name: 'blocks' }) }));
191008
+ return (jsx(FormsList, { platformKey: platformKey, authSpaUrl: authSpaUrl, currentSpa: currentSpa, onViewSubmissions: (form) => setView({ name: 'submissions', form }), onEditForm: (formId) => setView({ name: 'editor', formId, startNew: false }), onNewForm: () => setView({ name: 'editor', formId: null, startNew: true }), onViewBlocks: () => setView({ name: 'blocks' }) }));
190988
191009
  }
190989
191010
  /** The entry view: every application form of the platform, with actions. */
190990
- function FormsList({ platformKey, authSpaUrl, onViewSubmissions, onEditForm, onNewForm, onViewBlocks, }) {
191011
+ function FormsList({ platformKey, authSpaUrl, currentSpa, onViewSubmissions, onEditForm, onNewForm, onViewBlocks, }) {
190991
191012
  const t = useT();
190992
191013
  /** The form id whose link was just copied (transient "Copied" feedback). */
190993
191014
  const [copiedFormId, setCopiedFormId] = useState(null);
@@ -191001,7 +191022,7 @@ function FormsList({ platformKey, authSpaUrl, onViewSubmissions, onEditForm, onN
191001
191022
  const counts = statsCounts(stats);
191002
191023
  const total = Object.values(counts).reduce((sum, n) => sum + n, 0);
191003
191024
  const handleCopyLink = async (form) => {
191004
- const link = buildApplicationLink(platformKey, form.id, authSpaUrl);
191025
+ const link = buildApplicationLink(platformKey, form.id, authSpaUrl, undefined, currentSpa);
191005
191026
  if (!link)
191006
191027
  return;
191007
191028
  try {
@@ -191021,7 +191042,7 @@ function FormsList({ platformKey, authSpaUrl, onViewSubmissions, onEditForm, onN
191021
191042
  ? 'bg-blue-50 text-blue-700 border-blue-200 dark:bg-blue-900/30 dark:text-blue-300 dark:border-blue-900'
191022
191043
  : 'bg-gray-100 text-gray-600 border-gray-200 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-600', children: t(form.enabled
191023
191044
  ? 'profileApplicationsTab.formEnabled'
191024
- : 'profileApplicationsTab.formDisabled') }) }), jsx("td", { className: "whitespace-nowrap px-6 py-4 text-right", children: jsxs("div", { className: "flex items-center justify-end gap-1", children: [buildApplicationLink(platformKey, form.id, authSpaUrl) && (jsxs(Tooltip$1, { children: [jsx(TooltipTrigger, { asChild: true, children: jsxs(Button$1, { variant: "ghost", className: copiedFormId === form.id
191045
+ : 'profileApplicationsTab.formDisabled') }) }), jsx("td", { className: "whitespace-nowrap px-6 py-4 text-right", children: jsxs("div", { className: "flex items-center justify-end gap-1", children: [buildApplicationLink(platformKey, form.id, authSpaUrl, undefined, currentSpa) && (jsxs(Tooltip$1, { children: [jsx(TooltipTrigger, { asChild: true, children: jsxs(Button$1, { variant: "ghost", className: copiedFormId === form.id
191025
191046
  ? 'h-8 w-8 p-0 text-blue-600 dark:text-blue-400'
191026
191047
  : 'h-8 w-8 p-0 text-gray-500 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-100', onClick: () => handleCopyLink(form), "aria-label": t('profileApplicationsTab.copyFormLink', {
191027
191048
  form: ((_b = form.schema) === null || _b === void 0 ? void 0 : _b.title) ||
@@ -191574,7 +191595,7 @@ const Admin = ({ tenant, onInviteClick, hasUserTabPermission = false, hasGroupsT
191574
191595
  */
191575
191596
  const isWatcher = isPureWatcher(storeRbacPermissions, currentTenant);
191576
191597
  const showUsersTab = isWatcher || hasUserTabPermission;
191577
- return (jsx("div", { className: "border border-gray-200 rounded-lg p-6", children: jsxs(Tabs, { defaultValue: "users", "aria-label": t('profileAdmin.rbacManagementTabsAriaLabel'), children: [jsxs(TabsList, { "aria-label": t('profileAdmin.managementTabsAriaLabel'), className: "max-w-full overflow-x-auto justify-start px-1", children: [showUsersTab && jsx(TabsTrigger, { value: "users", children: t('profileAdmin.usersTab') }), !isWatcher && hasGroupsTabPermission && (jsx(TabsTrigger, { value: "groups", children: t('profileAdmin.groupsTab') })), !isWatcher && hasRolesTabPermission && (jsx(TabsTrigger, { value: "roles", children: t('profileAdmin.rolesTab') })), !isWatcher && hasPoliciesTabPermission && (jsx(TabsTrigger, { value: "policies", children: t('profileAdmin.policiesTab') })), !isWatcher && hasTeamsTabPermission && (jsx(TabsTrigger, { value: "teams", children: t('profileAdmin.teamsTab') })), !isWatcher && hasAlertsTabPermission && (jsx(TabsTrigger, { value: "alerts", children: t('profileAdmin.alertsTab') })), !isWatcher && (jsx(TabsTrigger, { value: "applications", children: t('profileAdmin.applicationsTab') })), !isWatcher && (jsx(TabsTrigger, { value: "onboarding", children: t('profileAdmin.onboardingTab') }))] }), showUsersTab && (jsxs(TabsContent, { className: "mt-4", value: "users", children: [jsx(TabDescription, { icon: jsx(Users, { className: "w-5 h-5" }), title: t('profileAdmin.usersTab'), children: t('profileAdmin.usersDescription') }), jsx(UsersTab, { tenant: tenant, onInviteClick: onInviteClick, currentSpa: currentSpa, showGradebookTab: showGradebookTab })] })), !isWatcher && hasGroupsTabPermission && (jsxs(TabsContent, { className: "mt-4", value: "groups", children: [jsx(TabDescription, { icon: jsx(UsersRound, { className: "w-5 h-5" }), title: t('profileAdmin.groupsTab'), children: t('profileAdmin.groupsDescription') }), jsx(GroupsTab, { tenant: tenant })] })), !isWatcher && hasRolesTabPermission && (jsxs(TabsContent, { className: "mt-4", value: "roles", children: [jsx(TabDescription, { icon: jsx(UserCog, { className: "w-5 h-5" }), title: t('profileAdmin.rolesTab'), children: t('profileAdmin.rolesDescription') }), jsx(RolesTab, { tenant: tenant })] })), !isWatcher && hasPoliciesTabPermission && (jsxs(TabsContent, { className: "mt-4", value: "policies", children: [jsx(TabDescription, { icon: jsx(ScrollText, { className: "w-5 h-5" }), title: t('profileAdmin.policiesTab'), children: t('profileAdmin.policiesDescription') }), jsx(PoliciesTab, { tenant: tenant })] })), !isWatcher && hasTeamsTabPermission && (jsxs(TabsContent, { className: "mt-4", value: "teams", children: [jsx(TabDescription, { icon: jsx(Shield, { className: "w-5 h-5" }), title: t('profileAdmin.teamsTab'), children: t('profileAdmin.teamsDescription') }), jsx(GroupsTab, { tenant: tenant, isTeam: true, onInviteClick: onInviteClick, hasInviteUserPermission: hasInviteUserPermission, hasCreateTeamPermission: hasCreateTeamPermission, onLoadGroupPermissions: onLoadGroupPermissions, rbacPermissions: rbacPermissions, enableRbac: enableRbac })] })), !isWatcher && hasAlertsTabPermission && (jsxs(TabsContent, { className: "mt-4", value: "alerts", children: [jsx(TabDescription, { icon: jsx(BellRing, { className: "w-5 h-5" }), title: t('profileAdmin.alertsTab'), children: t('profileAdmin.alertsDescription') }), jsx(AlertsTab$1, { tenant: tenant })] })), !isWatcher && (jsxs(TabsContent, { className: "mt-4", value: "applications", children: [jsx(TabDescription, { icon: jsx(ClipboardList, { className: "w-5 h-5" }), title: t('profileAdmin.applicationsTab'), children: t('profileAdmin.applicationsTabDescription') }), jsx(ApplicationsTab, { platformKey: tenant, authSpaUrl: authSpaUrl })] })), !isWatcher && (jsxs(TabsContent, { className: "mt-4", value: "onboarding", children: [jsx(TabDescription, { icon: jsx(UserPlus, { className: "w-5 h-5" }), title: t('profileAdmin.onboardingTab'), children: t('profileAdmin.onboardingTabDescription') }), jsx(OnboardingTab, { platformKey: tenant, onboardingBasePath: onboardingBasePath })] }))] }) }));
191598
+ return (jsx("div", { className: "border border-gray-200 rounded-lg p-6", children: jsxs(Tabs, { defaultValue: "users", "aria-label": t('profileAdmin.rbacManagementTabsAriaLabel'), children: [jsxs(TabsList, { "aria-label": t('profileAdmin.managementTabsAriaLabel'), className: "max-w-full overflow-x-auto justify-start px-1", children: [showUsersTab && jsx(TabsTrigger, { value: "users", children: t('profileAdmin.usersTab') }), !isWatcher && hasGroupsTabPermission && (jsx(TabsTrigger, { value: "groups", children: t('profileAdmin.groupsTab') })), !isWatcher && hasRolesTabPermission && (jsx(TabsTrigger, { value: "roles", children: t('profileAdmin.rolesTab') })), !isWatcher && hasPoliciesTabPermission && (jsx(TabsTrigger, { value: "policies", children: t('profileAdmin.policiesTab') })), !isWatcher && hasTeamsTabPermission && (jsx(TabsTrigger, { value: "teams", children: t('profileAdmin.teamsTab') })), !isWatcher && hasAlertsTabPermission && (jsx(TabsTrigger, { value: "alerts", children: t('profileAdmin.alertsTab') })), !isWatcher && (jsx(TabsTrigger, { value: "applications", children: t('profileAdmin.applicationsTab') })), !isWatcher && (jsx(TabsTrigger, { value: "onboarding", children: t('profileAdmin.onboardingTab') }))] }), showUsersTab && (jsxs(TabsContent, { className: "mt-4", value: "users", children: [jsx(TabDescription, { icon: jsx(Users, { className: "w-5 h-5" }), title: t('profileAdmin.usersTab'), children: t('profileAdmin.usersDescription') }), jsx(UsersTab, { tenant: tenant, onInviteClick: onInviteClick, currentSpa: currentSpa, showGradebookTab: showGradebookTab })] })), !isWatcher && hasGroupsTabPermission && (jsxs(TabsContent, { className: "mt-4", value: "groups", children: [jsx(TabDescription, { icon: jsx(UsersRound, { className: "w-5 h-5" }), title: t('profileAdmin.groupsTab'), children: t('profileAdmin.groupsDescription') }), jsx(GroupsTab, { tenant: tenant })] })), !isWatcher && hasRolesTabPermission && (jsxs(TabsContent, { className: "mt-4", value: "roles", children: [jsx(TabDescription, { icon: jsx(UserCog, { className: "w-5 h-5" }), title: t('profileAdmin.rolesTab'), children: t('profileAdmin.rolesDescription') }), jsx(RolesTab, { tenant: tenant })] })), !isWatcher && hasPoliciesTabPermission && (jsxs(TabsContent, { className: "mt-4", value: "policies", children: [jsx(TabDescription, { icon: jsx(ScrollText, { className: "w-5 h-5" }), title: t('profileAdmin.policiesTab'), children: t('profileAdmin.policiesDescription') }), jsx(PoliciesTab, { tenant: tenant })] })), !isWatcher && hasTeamsTabPermission && (jsxs(TabsContent, { className: "mt-4", value: "teams", children: [jsx(TabDescription, { icon: jsx(Shield, { className: "w-5 h-5" }), title: t('profileAdmin.teamsTab'), children: t('profileAdmin.teamsDescription') }), jsx(GroupsTab, { tenant: tenant, isTeam: true, onInviteClick: onInviteClick, hasInviteUserPermission: hasInviteUserPermission, hasCreateTeamPermission: hasCreateTeamPermission, onLoadGroupPermissions: onLoadGroupPermissions, rbacPermissions: rbacPermissions, enableRbac: enableRbac })] })), !isWatcher && hasAlertsTabPermission && (jsxs(TabsContent, { className: "mt-4", value: "alerts", children: [jsx(TabDescription, { icon: jsx(BellRing, { className: "w-5 h-5" }), title: t('profileAdmin.alertsTab'), children: t('profileAdmin.alertsDescription') }), jsx(AlertsTab$1, { tenant: tenant })] })), !isWatcher && (jsxs(TabsContent, { className: "mt-4", value: "applications", children: [jsx(TabDescription, { icon: jsx(ClipboardList, { className: "w-5 h-5" }), title: t('profileAdmin.applicationsTab'), children: t('profileAdmin.applicationsTabDescription') }), jsx(ApplicationsTab, { platformKey: tenant, authSpaUrl: authSpaUrl, currentSpa: currentSpa })] })), !isWatcher && (jsxs(TabsContent, { className: "mt-4", value: "onboarding", children: [jsx(TabDescription, { icon: jsx(UserPlus, { className: "w-5 h-5" }), title: t('profileAdmin.onboardingTab'), children: t('profileAdmin.onboardingTabDescription') }), jsx(OnboardingTab, { platformKey: tenant, onboardingBasePath: onboardingBasePath })] }))] }) }));
191578
191599
  };
191579
191600
 
191580
191601
  function DeleteApiModal({ isOpen, onClose, apiKey, tenantKey }) {
@@ -218197,7 +218197,7 @@ const STATE_BADGE_CLASSES = {
218197
218197
  expired: 'bg-gray-50 text-gray-600',
218198
218198
  };
218199
218199
  const ACTIVE_BADGE_CLASSES = 'bg-blue-50 text-blue-700';
218200
- const timeAgo$1 = (value) => {
218200
+ const timeAgo = (value) => {
218201
218201
  if (!value)
218202
218202
  return '';
218203
218203
  const date = new Date(value);
@@ -218247,7 +218247,7 @@ function ReportHistory({ org, username, onDownload }) {
218247
218247
  const state = (_a = report.state) !== null && _a !== void 0 ? _a : '';
218248
218248
  const active = isActiveState$1(state);
218249
218249
  const labelKey = (_b = STATE_LABEL_KEYS[state]) !== null && _b !== void 0 ? _b : 'statePending';
218250
- return (jsxs(TableRow$1, { className: "border-b border-neutral-200", children: [jsx(TableCell$1, { className: "p-3 whitespace-nowrap", children: jsxs(Badge, { variant: "outline", className: cn(active ? ACTIVE_BADGE_CLASSES : (_c = STATE_BADGE_CLASSES[state]) !== null && _c !== void 0 ? _c : ''), children: [active && (jsx(LoaderCircle, { className: "mr-1 h-3 w-3 animate-spin", "aria-hidden": "true" })), t(`chatHistoryReportHistory.${labelKey}`)] }) }), jsxs(TableCell$1, { className: "p-3 whitespace-nowrap text-[#646464]", children: [timeAgo$1(report.created_at), report.owner && report.owner !== username && (jsx("div", { className: "text-xs text-gray-500", children: t('chatHistoryReportHistory.createdBy', { owner: report.owner }) }))] }), jsx(TableCell$1, { className: "max-w-[280px] truncate p-3 text-[#646464]", children: filterSummary(report, t) }), jsx(TableCell$1, { className: "p-3 whitespace-nowrap text-[#646464]", children: state === 'completed' && report.expires ? timeAgo$1(report.expires) : '—' }), jsx(TableCell$1, { className: "p-3 text-right whitespace-nowrap", children: state === 'completed' && report.url && (jsxs(Button$1, { variant: "outline", size: "sm", className: "flex items-center gap-2 bg-transparent", onClick: () => handleDownload(report), disabled: downloadingId === report.report_id, children: [downloadingId === report.report_id ? (jsx(LoaderCircle, { className: "h-4 w-4 animate-spin", "aria-hidden": "true" })) : (jsx(Download, { className: "h-4 w-4", "aria-hidden": "true" })), t('chatHistoryReportHistory.download')] })) })] }, (_d = report.report_id) !== null && _d !== void 0 ? _d : report.created_at));
218250
+ return (jsxs(TableRow$1, { className: "border-b border-neutral-200", children: [jsx(TableCell$1, { className: "p-3 whitespace-nowrap", children: jsxs(Badge, { variant: "outline", className: cn(active ? ACTIVE_BADGE_CLASSES : (_c = STATE_BADGE_CLASSES[state]) !== null && _c !== void 0 ? _c : ''), children: [active && (jsx(LoaderCircle, { className: "mr-1 h-3 w-3 animate-spin", "aria-hidden": "true" })), t(`chatHistoryReportHistory.${labelKey}`)] }) }), jsxs(TableCell$1, { className: "p-3 whitespace-nowrap text-[#646464]", children: [timeAgo(report.created_at), report.owner && report.owner !== username && (jsx("div", { className: "text-xs text-gray-500", children: t('chatHistoryReportHistory.createdBy', { owner: report.owner }) }))] }), jsx(TableCell$1, { className: "max-w-[280px] truncate p-3 text-[#646464]", children: filterSummary(report, t) }), jsx(TableCell$1, { className: "p-3 whitespace-nowrap text-[#646464]", children: state === 'completed' && report.expires ? timeAgo(report.expires) : '—' }), jsx(TableCell$1, { className: "p-3 text-right whitespace-nowrap", children: state === 'completed' && report.url && (jsxs(Button$1, { variant: "outline", size: "sm", className: "flex items-center gap-2 bg-transparent", onClick: () => handleDownload(report), disabled: downloadingId === report.report_id, children: [downloadingId === report.report_id ? (jsx(LoaderCircle, { className: "h-4 w-4 animate-spin", "aria-hidden": "true" })) : (jsx(Download, { className: "h-4 w-4", "aria-hidden": "true" })), t('chatHistoryReportHistory.download')] })) })] }, (_d = report.report_id) !== null && _d !== void 0 ? _d : report.created_at));
218251
218251
  })) })] })) }) }) }));
218252
218252
  }
218253
218253
 
@@ -218462,11 +218462,17 @@ function stripMarkdown$1(text) {
218462
218462
  .replace(/\s+/g, ' ')
218463
218463
  .trim();
218464
218464
  }
218465
- const timeAgo = (value) => {
218465
+ /**
218466
+ * When a conversation happened: the calendar date and time, followed by the
218467
+ * relative "x ago" so both the exact day and recency read at a glance.
218468
+ */
218469
+ const formatSessionDate = (value) => {
218466
218470
  if (!value)
218467
218471
  return '';
218468
218472
  const date = new Date(value);
218469
- return Number.isNaN(date.getTime()) ? '' : formatDistanceToNow(date, { addSuffix: true });
218473
+ if (Number.isNaN(date.getTime()))
218474
+ return '';
218475
+ return `${format$1(date, 'MMM d, yyyy, h:mm a')} · ${formatDistanceToNow(date, { addSuffix: true })}`;
218470
218476
  };
218471
218477
  /**
218472
218478
  * The list endpoint may serialize `messages` as a JSON string while the detail
@@ -218567,11 +218573,18 @@ function AgentMessageAvatar({ agent, fallback }) {
218567
218573
  return (jsxs(Avatar, { className: "h-8 w-8 flex-shrink-0", children: [jsx(AvatarImage, { src: agent.profileImage, alt: "" }), jsx(AvatarFallback, { className: "bg-gray-100 text-sm font-medium text-gray-600", children: ((_b = (_a = agent.name) === null || _a === void 0 ? void 0 : _a.charAt(0)) === null || _b === void 0 ? void 0 : _b.toUpperCase()) || fallback })] }));
218568
218574
  }
218569
218575
  /** Messages of one conversation, in the agent-settings History tab's style. */
218570
- function ConversationMessages({ messages, username, agent, }) {
218576
+ function ConversationMessages({ messages, username, agent, fallbackEmail, }) {
218571
218577
  const t = useT();
218578
+ // On your own profile the human turns are "You"; an admin or watcher
218579
+ // reading someone else's history sees that user's email instead.
218580
+ const isPreview = useIsProfilePreview(username);
218581
+ const { data: userMetadata } = useGetUserMetadataQuery({ params: { username } });
218582
+ const humanLabel = isPreview
218583
+ ? (userMetadata === null || userMetadata === void 0 ? void 0 : userMetadata.email) || fallbackEmail || username
218584
+ : t('chatHistoryChatHistoryTab.you');
218572
218585
  return (jsx("div", { className: "space-y-6", children: messages.map((message, index) => {
218573
218586
  var _a, _b;
218574
- return (jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-start gap-3", children: [jsx(UserMessageAvatar, { username: username }), jsxs("div", { className: "min-w-0 flex-1 overflow-x-hidden", children: [jsx("div", { className: "font-medium text-gray-700", children: t('chatHistoryChatHistoryTab.you') }), jsx(Markdown, { className: "mt-1 text-sm text-gray-500", children: (_a = message.human) !== null && _a !== void 0 ? _a : '' })] })] }), jsxs("div", { className: "flex items-start gap-3 border-t border-gray-200 pt-4", children: [jsx(AgentMessageAvatar, { agent: agent, fallback: t('chatHistoryChatHistoryTab.aiAvatarLabel') }), jsxs("div", { className: "min-w-0 flex-1 overflow-x-hidden", children: [jsx("div", { className: "font-medium text-gray-700", children: agent.name }), jsx(Markdown, { className: "mt-1 text-sm text-gray-500", children: (_b = message.ai) !== null && _b !== void 0 ? _b : '' })] })] })] }, index));
218587
+ return (jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-start gap-3", children: [jsx(UserMessageAvatar, { username: username }), jsxs("div", { className: "min-w-0 flex-1 overflow-x-hidden", children: [jsx("div", { className: "font-medium break-all text-gray-700", children: humanLabel }), jsx(Markdown, { className: "mt-1 text-sm text-gray-500", children: (_a = message.human) !== null && _a !== void 0 ? _a : '' })] })] }), jsxs("div", { className: "flex items-start gap-3 border-t border-gray-200 pt-4", children: [jsx(AgentMessageAvatar, { agent: agent, fallback: t('chatHistoryChatHistoryTab.aiAvatarLabel') }), jsxs("div", { className: "min-w-0 flex-1 overflow-x-hidden", children: [jsx("div", { className: "font-medium text-gray-700", children: agent.name }), jsx(Markdown, { className: "mt-1 text-sm text-gray-500", children: (_b = message.ai) !== null && _b !== void 0 ? _b : '' })] })] })] }, index));
218575
218588
  }) }));
218576
218589
  }
218577
218590
  /**
@@ -218589,7 +218602,7 @@ function ConversationPreview({ org, username, session, agent, }) {
218589
218602
  if (isLoading) {
218590
218603
  return (jsx("div", { className: "flex items-center justify-center py-10", children: jsx(Spinner, {}) }));
218591
218604
  }
218592
- return (jsxs(Fragment$1, { children: [jsxs("div", { className: "mb-4 flex items-start justify-between gap-3", children: [jsxs("div", { className: "min-w-0", children: [jsx("h3", { className: "mb-1 text-base font-semibold text-gray-700", children: textTruncate$2(title, 80) }), jsx("span", { className: "text-sm text-gray-500", children: timeAgo(session.inserted_at) })] }), messages.length > 0 && (jsxs(Button$1, { variant: "outline", size: "sm", className: "flex flex-shrink-0 items-center gap-2 bg-transparent", onClick: () => downloadTextFile(`conversation-${session.id}.csv`, conversationToCsv(session, messages, agent.name), 'text/csv'), children: [jsx(Download, { className: "h-4 w-4" }), t('chatHistoryChatHistoryTab.download')] }))] }), messages.length > 0 ? (jsx(ConversationMessages, { messages: messages, username: username, agent: agent })) : (jsx("p", { className: "text-sm text-gray-500", children: t('chatHistoryChatHistoryTab.noMessages') }))] }));
218605
+ return (jsxs(Fragment$1, { children: [jsxs("div", { className: "mb-4 flex items-start justify-between gap-3", children: [jsxs("div", { className: "min-w-0", children: [jsx("h3", { className: "mb-1 text-base font-semibold text-gray-700", children: textTruncate$2(title, 80) }), jsx("span", { className: "text-sm text-gray-500", children: formatSessionDate(session.inserted_at) })] }), messages.length > 0 && (jsxs(Button$1, { variant: "outline", size: "sm", className: "flex flex-shrink-0 items-center gap-2 bg-transparent", onClick: () => downloadTextFile(`conversation-${session.id}.csv`, conversationToCsv(session, messages, agent.name), 'text/csv'), children: [jsx(Download, { className: "h-4 w-4" }), t('chatHistoryChatHistoryTab.download')] }))] }), messages.length > 0 ? (jsx(ConversationMessages, { messages: messages, username: username, agent: agent, fallbackEmail: session.email })) : (jsx("p", { className: "text-sm text-gray-500", children: t('chatHistoryChatHistoryTab.noMessages') }))] }));
218593
218606
  }
218594
218607
  /**
218595
218608
  * Profile-level view of the user's own conversations across every agent —
@@ -218670,7 +218683,7 @@ function ChatHistoryTab({ org, username }) {
218670
218683
  event.preventDefault();
218671
218684
  handleConversationClick(session);
218672
218685
  }
218673
- }, children: [jsxs("div", { className: "w-full", children: [jsxs("div", { className: "mb-2 flex items-center justify-between gap-2", children: [jsxs("span", { className: "flex items-center gap-2 text-sm text-gray-600", children: [timeAgo(session.inserted_at), sentiment && (jsx(Badge, { variant: "outline", className: cn('font-normal', (_d = SENTIMENT_CLASSES[sentiment]) !== null && _d !== void 0 ? _d : SENTIMENT_CLASSES.neutral), children: sentimentLabel((_e = session.sentiment) !== null && _e !== void 0 ? _e : '') }))] }), jsx("span", { className: "max-w-[100px] truncate text-sm text-gray-900 sm:max-w-[200px]", children: resolveAgent(session).name })] }), jsx("div", { className: "font-medium text-gray-900", children: textTruncate$2(title, 50) }), preview && (jsx("p", { className: "line-clamp-1 text-sm text-gray-600", children: textTruncate$2(preview, 60) }))] }), jsx("div", { className: "flex items-center gap-2 text-sm text-gray-600", children: jsx(ChevronRight, { className: "h-4 w-4" }) })] }, session.id));
218686
+ }, children: [jsxs("div", { className: "w-full", children: [jsxs("div", { className: "mb-2 flex items-center justify-between gap-2", children: [jsxs("span", { className: "flex items-center gap-2 text-sm text-gray-600", children: [formatSessionDate(session.inserted_at), sentiment && (jsx(Badge, { variant: "outline", className: cn('font-normal', (_d = SENTIMENT_CLASSES[sentiment]) !== null && _d !== void 0 ? _d : SENTIMENT_CLASSES.neutral), children: sentimentLabel((_e = session.sentiment) !== null && _e !== void 0 ? _e : '') }))] }), jsx("span", { className: "max-w-[100px] truncate text-sm text-gray-900 sm:max-w-[200px]", children: resolveAgent(session).name })] }), jsx("div", { className: "font-medium text-gray-900", children: textTruncate$2(title, 50) }), preview && (jsx("p", { className: "line-clamp-1 text-sm text-gray-600", children: textTruncate$2(preview, 60) }))] }), jsx("div", { className: "flex items-center gap-2 text-sm text-gray-600", children: jsx(ChevronRight, { className: "h-4 w-4" }) })] }, session.id));
218674
218687
  }) }), totalPages > 1 && (jsx("div", { className: "flex-shrink-0", children: jsx(IblPagination, { currentPage: page, totalPages: totalPages, onPageChange: setPage, disabled: isFetching || isLoading, disableNumberedButtons: isMobile }) }))] }), jsx("div", { className: "scrollbar-hide hidden min-h-0 flex-col overflow-y-auto rounded-lg border border-neutral-200 bg-white p-6 shadow-sm md:flex", tabIndex: 0, role: "region", "aria-label": t('chatHistoryChatHistoryTab.conversationPreviewLabel'), children: selectedSession ? (jsx(ConversationPreview, { org: org, username: username, session: selectedSession, agent: resolveAgent(selectedSession) })) : (jsx("div", { className: "flex h-full items-center justify-center text-gray-500", children: t('chatHistoryChatHistoryTab.selectPrompt') })) })] })), jsx(Dialog, { open: isPreviewModalOpen, onOpenChange: setIsPreviewModalOpen, 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: selectedSession
218675
218688
  ? stripMarkdown$1(selectedSession.title || t('chatHistoryChatHistoryTab.conversation'))
218676
218689
  : t('chatHistoryChatHistoryTab.conversation') }) }), jsx("div", { className: "mx-auto max-w-full overflow-x-hidden", children: selectedSession && (jsx(ConversationPreview, { org: org, username: username, session: selectedSession, agent: resolveAgent(selectedSession) })) })] }) })] }), jsx(TabsContent, { value: "exports", className: "mt-0 min-h-0 flex-1 overflow-y-auto data-[state=inactive]:hidden", children: jsx(ReportHistory, { org: org, username: username, onDownload: downloadFromUrl }) })] }));
@@ -223564,15 +223577,17 @@ const PAGE_SIZE$b = 20;
223564
223577
  const SEARCH_DEBOUNCE_MS$1 = 300;
223565
223578
  /**
223566
223579
  * The public application URL on the auth SPA for one form of a platform
223567
- * (`<auth-url>/applications/<platform-key>/<form-id>?redirect-to=<origin>`), or
223568
- * null when the auth SPA URL cannot be resolved. `authSpaUrl` (prop) wins;
223580
+ * (`<auth-url>/applications/<platform-key>/<form-id>?redirect-to=<origin>&app=<spa>`),
223581
+ * or null when the auth SPA URL cannot be resolved. `authSpaUrl` (prop) wins;
223569
223582
  * otherwise the runtime-injected env (`window.__ENV__.NEXT_PUBLIC_AUTH_URL`) is
223570
223583
  * used. With no `formId` the link resolves the platform's default form (404
223571
223584
  * when none). The `redirect-to` param carries the origin the applicant is sent
223572
223585
  * back to after submitting — by default the origin the link is copied from — and
223573
- * is omitted when no origin can be read (no DOM).
223586
+ * is omitted when no origin can be read (no DOM). The `app` param carries the
223587
+ * SPA the link was copied from, so the login flow the applicant is sent through
223588
+ * (unauthenticated visitors) is branded for that SPA; omitted when unknown.
223574
223589
  */
223575
- function buildApplicationLink(platformKey, formId, authSpaUrl, redirectTo) {
223590
+ function buildApplicationLink(platformKey, formId, authSpaUrl, redirectTo, currentSpa) {
223576
223591
  var _a, _b;
223577
223592
  const base = (_b = authSpaUrl !== null && authSpaUrl !== void 0 ? authSpaUrl : (typeof window !== 'undefined'
223578
223593
  ? (_a = window.__ENV__) === null || _a === void 0 ? void 0 : _a.NEXT_PUBLIC_AUTH_URL
@@ -223582,7 +223597,13 @@ function buildApplicationLink(platformKey, formId, authSpaUrl, redirectTo) {
223582
223597
  const root = `${trimTrailingSlashes(base)}/applications/${encodeURIComponent(platformKey)}`;
223583
223598
  const path = formId != null ? `${root}/${encodeURIComponent(String(formId))}` : root;
223584
223599
  const target = (typeof window !== 'undefined' ? window.location.origin : undefined);
223585
- return target ? `${path}?redirect-to=${encodeURIComponent(target)}` : path;
223600
+ const query = new URLSearchParams();
223601
+ if (target)
223602
+ query.set('redirect-to', target);
223603
+ if (currentSpa)
223604
+ query.set('app', currentSpa);
223605
+ const search = query.toString();
223606
+ return search ? `${path}?${search}` : path;
223586
223607
  }
223587
223608
  /**
223588
223609
  * Applications management. Entry view lists the platform's application forms
@@ -223590,7 +223611,7 @@ function buildApplicationLink(platformKey, formId, authSpaUrl, redirectTo) {
223590
223611
  * per-form (or all-form) submission pipelines, the submission workspace with
223591
223612
  * per-student decisions, the form/question editor, and the ban list.
223592
223613
  */
223593
- function ApplicationsTab({ platformKey, authSpaUrl, }) {
223614
+ function ApplicationsTab({ platformKey, authSpaUrl, currentSpa, }) {
223594
223615
  const [view, setView] = useState({ name: 'forms' });
223595
223616
  if (view.name === 'editor') {
223596
223617
  return (jsx(QuestionsEditor, { platformKey: platformKey, initialFormId: view.formId, startNew: view.startNew, onBack: () => setView({ name: 'forms' }) }));
@@ -223604,10 +223625,10 @@ function ApplicationsTab({ platformKey, authSpaUrl, }) {
223604
223625
  if (view.name === 'submissions') {
223605
223626
  return (jsx(SubmissionsPipeline, { platformKey: platformKey, form: view.form, onBack: () => setView({ name: 'forms' }), onOpen: (submissionId) => setView({ name: 'detail', submissionId, form: view.form }) }));
223606
223627
  }
223607
- return (jsx(FormsList, { platformKey: platformKey, authSpaUrl: authSpaUrl, onViewSubmissions: (form) => setView({ name: 'submissions', form }), onEditForm: (formId) => setView({ name: 'editor', formId, startNew: false }), onNewForm: () => setView({ name: 'editor', formId: null, startNew: true }), onViewBlocks: () => setView({ name: 'blocks' }) }));
223628
+ return (jsx(FormsList, { platformKey: platformKey, authSpaUrl: authSpaUrl, currentSpa: currentSpa, onViewSubmissions: (form) => setView({ name: 'submissions', form }), onEditForm: (formId) => setView({ name: 'editor', formId, startNew: false }), onNewForm: () => setView({ name: 'editor', formId: null, startNew: true }), onViewBlocks: () => setView({ name: 'blocks' }) }));
223608
223629
  }
223609
223630
  /** The entry view: every application form of the platform, with actions. */
223610
- function FormsList({ platformKey, authSpaUrl, onViewSubmissions, onEditForm, onNewForm, onViewBlocks, }) {
223631
+ function FormsList({ platformKey, authSpaUrl, currentSpa, onViewSubmissions, onEditForm, onNewForm, onViewBlocks, }) {
223611
223632
  const t = useT();
223612
223633
  /** The form id whose link was just copied (transient "Copied" feedback). */
223613
223634
  const [copiedFormId, setCopiedFormId] = useState(null);
@@ -223621,7 +223642,7 @@ function FormsList({ platformKey, authSpaUrl, onViewSubmissions, onEditForm, onN
223621
223642
  const counts = statsCounts(stats);
223622
223643
  const total = Object.values(counts).reduce((sum, n) => sum + n, 0);
223623
223644
  const handleCopyLink = async (form) => {
223624
- const link = buildApplicationLink(platformKey, form.id, authSpaUrl);
223645
+ const link = buildApplicationLink(platformKey, form.id, authSpaUrl, undefined, currentSpa);
223625
223646
  if (!link)
223626
223647
  return;
223627
223648
  try {
@@ -223641,7 +223662,7 @@ function FormsList({ platformKey, authSpaUrl, onViewSubmissions, onEditForm, onN
223641
223662
  ? 'bg-blue-50 text-blue-700 border-blue-200 dark:bg-blue-900/30 dark:text-blue-300 dark:border-blue-900'
223642
223663
  : 'bg-gray-100 text-gray-600 border-gray-200 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-600', children: t(form.enabled
223643
223664
  ? 'profileApplicationsTab.formEnabled'
223644
- : 'profileApplicationsTab.formDisabled') }) }), jsx("td", { className: "whitespace-nowrap px-6 py-4 text-right", children: jsxs("div", { className: "flex items-center justify-end gap-1", children: [buildApplicationLink(platformKey, form.id, authSpaUrl) && (jsxs(Tooltip$1, { children: [jsx(TooltipTrigger, { asChild: true, children: jsxs(Button$1, { variant: "ghost", className: copiedFormId === form.id
223665
+ : 'profileApplicationsTab.formDisabled') }) }), jsx("td", { className: "whitespace-nowrap px-6 py-4 text-right", children: jsxs("div", { className: "flex items-center justify-end gap-1", children: [buildApplicationLink(platformKey, form.id, authSpaUrl, undefined, currentSpa) && (jsxs(Tooltip$1, { children: [jsx(TooltipTrigger, { asChild: true, children: jsxs(Button$1, { variant: "ghost", className: copiedFormId === form.id
223645
223666
  ? 'h-8 w-8 p-0 text-blue-600 dark:text-blue-400'
223646
223667
  : 'h-8 w-8 p-0 text-gray-500 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-100', onClick: () => handleCopyLink(form), "aria-label": t('profileApplicationsTab.copyFormLink', {
223647
223668
  form: ((_b = form.schema) === null || _b === void 0 ? void 0 : _b.title) ||
@@ -224110,7 +224131,7 @@ const Admin = ({ tenant, onInviteClick, hasUserTabPermission = false, hasGroupsT
224110
224131
  */
224111
224132
  const isWatcher = isPureWatcher(storeRbacPermissions, currentTenant);
224112
224133
  const showUsersTab = isWatcher || hasUserTabPermission;
224113
- return (jsx("div", { className: "border border-gray-200 rounded-lg p-6", children: jsxs(Tabs, { defaultValue: "users", "aria-label": t('profileAdmin.rbacManagementTabsAriaLabel'), children: [jsxs(TabsList, { "aria-label": t('profileAdmin.managementTabsAriaLabel'), className: "max-w-full overflow-x-auto justify-start px-1", children: [showUsersTab && jsx(TabsTrigger, { value: "users", children: t('profileAdmin.usersTab') }), !isWatcher && hasGroupsTabPermission && (jsx(TabsTrigger, { value: "groups", children: t('profileAdmin.groupsTab') })), !isWatcher && hasRolesTabPermission && (jsx(TabsTrigger, { value: "roles", children: t('profileAdmin.rolesTab') })), !isWatcher && hasPoliciesTabPermission && (jsx(TabsTrigger, { value: "policies", children: t('profileAdmin.policiesTab') })), !isWatcher && hasTeamsTabPermission && (jsx(TabsTrigger, { value: "teams", children: t('profileAdmin.teamsTab') })), !isWatcher && hasAlertsTabPermission && (jsx(TabsTrigger, { value: "alerts", children: t('profileAdmin.alertsTab') })), !isWatcher && (jsx(TabsTrigger, { value: "applications", children: t('profileAdmin.applicationsTab') })), !isWatcher && (jsx(TabsTrigger, { value: "onboarding", children: t('profileAdmin.onboardingTab') }))] }), showUsersTab && (jsxs(TabsContent, { className: "mt-4", value: "users", children: [jsx(TabDescription, { icon: jsx(Users, { className: "w-5 h-5" }), title: t('profileAdmin.usersTab'), children: t('profileAdmin.usersDescription') }), jsx(UsersTab, { tenant: tenant, onInviteClick: onInviteClick, currentSpa: currentSpa, showGradebookTab: showGradebookTab })] })), !isWatcher && hasGroupsTabPermission && (jsxs(TabsContent, { className: "mt-4", value: "groups", children: [jsx(TabDescription, { icon: jsx(UsersRound, { className: "w-5 h-5" }), title: t('profileAdmin.groupsTab'), children: t('profileAdmin.groupsDescription') }), jsx(GroupsTab, { tenant: tenant })] })), !isWatcher && hasRolesTabPermission && (jsxs(TabsContent, { className: "mt-4", value: "roles", children: [jsx(TabDescription, { icon: jsx(UserCog, { className: "w-5 h-5" }), title: t('profileAdmin.rolesTab'), children: t('profileAdmin.rolesDescription') }), jsx(RolesTab, { tenant: tenant })] })), !isWatcher && hasPoliciesTabPermission && (jsxs(TabsContent, { className: "mt-4", value: "policies", children: [jsx(TabDescription, { icon: jsx(ScrollText, { className: "w-5 h-5" }), title: t('profileAdmin.policiesTab'), children: t('profileAdmin.policiesDescription') }), jsx(PoliciesTab, { tenant: tenant })] })), !isWatcher && hasTeamsTabPermission && (jsxs(TabsContent, { className: "mt-4", value: "teams", children: [jsx(TabDescription, { icon: jsx(Shield, { className: "w-5 h-5" }), title: t('profileAdmin.teamsTab'), children: t('profileAdmin.teamsDescription') }), jsx(GroupsTab, { tenant: tenant, isTeam: true, onInviteClick: onInviteClick, hasInviteUserPermission: hasInviteUserPermission, hasCreateTeamPermission: hasCreateTeamPermission, onLoadGroupPermissions: onLoadGroupPermissions, rbacPermissions: rbacPermissions, enableRbac: enableRbac })] })), !isWatcher && hasAlertsTabPermission && (jsxs(TabsContent, { className: "mt-4", value: "alerts", children: [jsx(TabDescription, { icon: jsx(BellRing, { className: "w-5 h-5" }), title: t('profileAdmin.alertsTab'), children: t('profileAdmin.alertsDescription') }), jsx(AlertsTab, { tenant: tenant })] })), !isWatcher && (jsxs(TabsContent, { className: "mt-4", value: "applications", children: [jsx(TabDescription, { icon: jsx(ClipboardList, { className: "w-5 h-5" }), title: t('profileAdmin.applicationsTab'), children: t('profileAdmin.applicationsTabDescription') }), jsx(ApplicationsTab, { platformKey: tenant, authSpaUrl: authSpaUrl })] })), !isWatcher && (jsxs(TabsContent, { className: "mt-4", value: "onboarding", children: [jsx(TabDescription, { icon: jsx(UserPlus, { className: "w-5 h-5" }), title: t('profileAdmin.onboardingTab'), children: t('profileAdmin.onboardingTabDescription') }), jsx(OnboardingTab, { platformKey: tenant, onboardingBasePath: onboardingBasePath })] }))] }) }));
224134
+ return (jsx("div", { className: "border border-gray-200 rounded-lg p-6", children: jsxs(Tabs, { defaultValue: "users", "aria-label": t('profileAdmin.rbacManagementTabsAriaLabel'), children: [jsxs(TabsList, { "aria-label": t('profileAdmin.managementTabsAriaLabel'), className: "max-w-full overflow-x-auto justify-start px-1", children: [showUsersTab && jsx(TabsTrigger, { value: "users", children: t('profileAdmin.usersTab') }), !isWatcher && hasGroupsTabPermission && (jsx(TabsTrigger, { value: "groups", children: t('profileAdmin.groupsTab') })), !isWatcher && hasRolesTabPermission && (jsx(TabsTrigger, { value: "roles", children: t('profileAdmin.rolesTab') })), !isWatcher && hasPoliciesTabPermission && (jsx(TabsTrigger, { value: "policies", children: t('profileAdmin.policiesTab') })), !isWatcher && hasTeamsTabPermission && (jsx(TabsTrigger, { value: "teams", children: t('profileAdmin.teamsTab') })), !isWatcher && hasAlertsTabPermission && (jsx(TabsTrigger, { value: "alerts", children: t('profileAdmin.alertsTab') })), !isWatcher && (jsx(TabsTrigger, { value: "applications", children: t('profileAdmin.applicationsTab') })), !isWatcher && (jsx(TabsTrigger, { value: "onboarding", children: t('profileAdmin.onboardingTab') }))] }), showUsersTab && (jsxs(TabsContent, { className: "mt-4", value: "users", children: [jsx(TabDescription, { icon: jsx(Users, { className: "w-5 h-5" }), title: t('profileAdmin.usersTab'), children: t('profileAdmin.usersDescription') }), jsx(UsersTab, { tenant: tenant, onInviteClick: onInviteClick, currentSpa: currentSpa, showGradebookTab: showGradebookTab })] })), !isWatcher && hasGroupsTabPermission && (jsxs(TabsContent, { className: "mt-4", value: "groups", children: [jsx(TabDescription, { icon: jsx(UsersRound, { className: "w-5 h-5" }), title: t('profileAdmin.groupsTab'), children: t('profileAdmin.groupsDescription') }), jsx(GroupsTab, { tenant: tenant })] })), !isWatcher && hasRolesTabPermission && (jsxs(TabsContent, { className: "mt-4", value: "roles", children: [jsx(TabDescription, { icon: jsx(UserCog, { className: "w-5 h-5" }), title: t('profileAdmin.rolesTab'), children: t('profileAdmin.rolesDescription') }), jsx(RolesTab, { tenant: tenant })] })), !isWatcher && hasPoliciesTabPermission && (jsxs(TabsContent, { className: "mt-4", value: "policies", children: [jsx(TabDescription, { icon: jsx(ScrollText, { className: "w-5 h-5" }), title: t('profileAdmin.policiesTab'), children: t('profileAdmin.policiesDescription') }), jsx(PoliciesTab, { tenant: tenant })] })), !isWatcher && hasTeamsTabPermission && (jsxs(TabsContent, { className: "mt-4", value: "teams", children: [jsx(TabDescription, { icon: jsx(Shield, { className: "w-5 h-5" }), title: t('profileAdmin.teamsTab'), children: t('profileAdmin.teamsDescription') }), jsx(GroupsTab, { tenant: tenant, isTeam: true, onInviteClick: onInviteClick, hasInviteUserPermission: hasInviteUserPermission, hasCreateTeamPermission: hasCreateTeamPermission, onLoadGroupPermissions: onLoadGroupPermissions, rbacPermissions: rbacPermissions, enableRbac: enableRbac })] })), !isWatcher && hasAlertsTabPermission && (jsxs(TabsContent, { className: "mt-4", value: "alerts", children: [jsx(TabDescription, { icon: jsx(BellRing, { className: "w-5 h-5" }), title: t('profileAdmin.alertsTab'), children: t('profileAdmin.alertsDescription') }), jsx(AlertsTab, { tenant: tenant })] })), !isWatcher && (jsxs(TabsContent, { className: "mt-4", value: "applications", children: [jsx(TabDescription, { icon: jsx(ClipboardList, { className: "w-5 h-5" }), title: t('profileAdmin.applicationsTab'), children: t('profileAdmin.applicationsTabDescription') }), jsx(ApplicationsTab, { platformKey: tenant, authSpaUrl: authSpaUrl, currentSpa: currentSpa })] })), !isWatcher && (jsxs(TabsContent, { className: "mt-4", value: "onboarding", children: [jsx(TabDescription, { icon: jsx(UserPlus, { className: "w-5 h-5" }), title: t('profileAdmin.onboardingTab'), children: t('profileAdmin.onboardingTabDescription') }), jsx(OnboardingTab, { platformKey: tenant, onboardingBasePath: onboardingBasePath })] }))] }) }));
224114
224135
  };
224115
224136
 
224116
224137
  function DeleteApiModal$1({ isOpen, onClose, apiKey, tenantKey }) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iblai/iblai-js",
3
- "version": "2.10.2",
3
+ "version": "2.11.0",
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": {
@@ -77,7 +77,7 @@
77
77
  "dotenv": "16.6.1",
78
78
  "winston": "3.19.0",
79
79
  "@iblai/data-layer": "1.13.3",
80
- "@iblai/web-containers": "1.19.14",
80
+ "@iblai/web-containers": "1.20.0",
81
81
  "@iblai/web-utils": "2.4.6",
82
82
  "@iblai/mcp": "1.12.12"
83
83
  },