@iblai/iblai-js 2.17.0 → 2.18.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.
@@ -1,9 +1,9 @@
1
1
  import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
2
2
  import * as React from 'react';
3
- import React__default, { useState, useEffect, forwardRef, createElement as createElement$3, useLayoutEffect, createContext, useContext, useMemo, useReducer, useRef, useImperativeHandle, useCallback, isValidElement, Children, PureComponent, cloneElement, Component as Component$1, useId as useId$2, useDebugValue, lazy, Suspense } from 'react';
3
+ import React__default, { useState, useEffect, forwardRef, createElement as createElement$3, useLayoutEffect, createContext, useContext, useMemo, useReducer, useRef, useImperativeHandle, useCallback, isValidElement, Children, PureComponent, cloneElement, Component as Component$1, Fragment as Fragment$2, useId as useId$2, useDebugValue, lazy, Suspense } from 'react';
4
4
  import * as dataLayer from '@iblai/data-layer';
5
- import { useGetUserMetadataQuery, useGetUserMetadataEdxQuery, useGetUserProfileSummaryQuery, useUpdateUserMetadataMutation, useUpdateUserMetadataEdxMutation, useUploadProfileImageMutation, useResetPasswordMutation, useSelfRetireMutation, useCreateUserInstitutionMutation, useGetUserInstitutionsQuery, useCreateUserEducationMutation, useUpdateUserEducationMutation, useDeleteUserEducationMutation, useGetUserEducationQuery, useCreateUserCompanyMutation, useGetUserCompaniesQuery, useCreateUserExperienceMutation, useUpdateUserExperienceMutation, useDeleteUserExperienceMutation, useGetUserExperienceQuery, useGetUserResumeQuery, useCreateUserResumeMutation, useGetMySubscriptionsQuery, useGetItemSubscriptionQuery, useCancelSubscriptionMutation, useGetLearnerDetailsQuery, useGetUserEnrolledCoursesQuery, useGetUserEnrolledProgramsQuery, useGetUserCatalogPathwaysQuery, useGetUsersAsAssertionsQuery, useGetUserEarnedSkillsQuery, useGetUserReportedSkillsQuery, useGetUserDesiredSkillsQuery, useCreateGlobalMemoryMutation, useUpdateGlobalMemoryMutation, useUpdateMentorMemoryMutation, useGetMemsearchStatusQuery, useGetUserMemorySettingsQuery, useUpdateUserMemorySettingsMutation, useGetGlobalMemoriesQuery, useGetAllMentorMemoriesQuery, useDeleteGlobalMemoryMutation, useDeleteMentorMemoryMutation, useGetAiSearchMentorsQuery, useStarMentorMutation, useUnstarMentorMutation, useGetUserChatHistoryReportsQuery, USER_CHAT_HISTORY_REPORT_ACTIVE_STATES, useCreateUserChatHistoryReportMutation, useLazyGetDownloadReportFromURLQuery, useGetUserChatHistoryReportStatusQuery, userChatHistoryApiSlice, USER_CHAT_HISTORY_QUERY_KEYS, useGetUserChatHistoryQuery, useGetUserChatHistoryFilterQuery, useGetMentorsQuery, useGetUserChatHistorySessionQuery, useInviteUserMutation, usePlatformInvitationsQuery, useCreateCatalogInvitationCourseBulkMutation, useGetCatalogInvitationsCourseQuery, useLazyPlatformUsersQuery, useLazyPlatformUserGroupsQuery, useGetPersonnalizedSearchQuery, useCreateCatalogInvitationProgramBulkMutation, useGetCatalogInvitationsProgramQuery, useUpdateUserRoleMutation, useUpdateUserStatusMutation, useUpdatePlatformUserRoleWithPoliciesMutation, usePlatformUsersQuery, useGetAccessibleUsersQuery, isPoliciesResponse, platformApiSlice, featureTags, useLazyGetRbacTeamsAccessListQuery, useCreateRbacTeamsAccessMutation, useGetRbacGroupsQuery, usePlatformUserGroupsQuery, useCreateRbacGroupMutation, useUpdateRbacGroupMutation, useDeleteRbacGroupMutation, useCreatePlatformUserGroupMutation, useUpdatePlatformUserGroupMutation, useDeletePlatformUserGroupMutation, useGetRbacGroupDetailsQuery, useGetPlatformUserGroupDetailsQuery, useGetRbacPermissionsMutation, useGetRbacRolesQuery, useCreateRbacRoleMutation, useUpdateRbacRoleMutation, useDeleteRbacRoleMutation, useGetRbacRoleDetailsQuery, useGetRbacActionsDefinitionQuery, useGetRbacResourceDiscoveryQuery, useGetRbacResourceListQuery, useGetRbacPoliciesQuery, useCreateRbacPolicyMutation, useUpdateRbacPolicyMutation, useDeleteRbacPolicyMutation, useGetRbacPolicyDetailsQuery, useGetWatchedGroupsQuery, useCreateWatchedGroupMutation, useUpdateWatchedGroupMutation, useDeleteWatchedGroupMutation, useAddWatchedUserMutation, useRemoveWatchedUserMutation, useAddWatcherMutation, useUpdateWatcherMutation, useDeleteWatcherMutation, useGetWatchedUsersQuery, useGetWatchersQuery, WATCHER_NOTIFICATION_EVENTS, WATCHER_NOTIFICATION_EVENT_LABELS, useGetApplicationFormsQuery, useGetApplicationFormDetailQuery, useCreateApplicationFormMutation, useUpdateApplicationFormMutation, useGetApplicationAdminDetailQuery, useInviteApplicationCandidateMutation, useLazyGetApplicationAttachmentFileQuery, useCreateApplicationWaiverMutation, useDeleteApplicationWaiverMutation, useSubmitApplicationOverrideMutation, useUpdateApplicationPlacementMutation, useDeleteApplicationCourseAssignmentMutation, useCreateApplicationNoteMutation, useUpdateApplicationNoteMutation, useDeleteApplicationNoteMutation, useTransitionApplicationMutation, useLinkApplicationCandidateMutation, useCreateApplicationPlacementsMutation, useGetCustomMentorsQuery, useCreateApplicationCourseAssignmentsMutation, useGetApplicationBlocksQuery, useDeleteApplicationBlockMutation, useGetApplicationsPipelineStatsQuery, useGetApplicationsPipelineQuery, useGetTenantMetadataQuery, useUpdateTenantMetadataMutation, useLazyGetTenantMetadataQuery, useDeleteApiKeyMutation, useGetApiKeyQuery, useGetApiKeyFieldPermissionsQuery, useUpdateApiKeyMutation, useGetApiKeysQuery, unwrapApiTokenList, useCreateApiKeyMutation, useCreateLLMCredentialMutation, useGetCredentialsSchemaQuery, useGetMaskedLLMCredentialsQuery, useGetLlmsQuery, useDeleteIntegrationCredentialMutation, useDeleteCredentialMutation, useCreateIntegrationCredentialMutation, useGetIntegrationCredentialsSchemaQuery, useGetMaskedIntegrationCredentialsQuery, useGetAccountBillingInfoQuery, useUpdateAutoRechargeInfoMutation, useTriggerAutoRechargeMutation, useGetAgentSpendCapQuery, useGetTenantSpendCapQuery, useListAgentUserSpendCapsQuery, useUpsertAgentSpendCapMutation, useDeleteAgentSpendCapMutation, useUpsertTenantSpendCapMutation, useDeleteTenantSpendCapMutation, useUpsertUserSpendCapMutation, useDeleteUserSpendCapMutation, useGetLlmUsageMetricsQuery, useListAgentSpendCapsQuery, useGetCreditTransactionsQuery, useCreateStripeCustomerPortalMutation, useSetPlatformConfigurationsMutation, useGetPlatformConfigurationsQuery, useUpdatePlatformMembershipMutation, useGetPlatformMembershipQuery, useGetCustomDomainsQuery, useCreateCustomDomainMutation, useDeleteCustomDomainMutation, useGetStudentMentorCreationStatusQuery, useSetStudentMentorCreationStatusMutation, coreApiSlice, recommendationPromptTypeEnum, useGetRecommendedPromptsListQuery, useCreateRecommendedPromptMutation, useUpdateRecommendedPromptMutation, useDeleteRecommendedPromptMutation, useLazyGetPublicPlatformImageAssetFileUrlQuery, useCreatePlatformImageAssetMutation, useGetProviderConfigQuery, useCreateProviderConfigMutation, useDeleteProviderConfigMutation, useGetExternalMappingQuery, useGetCredentialsListQuery, useCreateExternalMappingMutation, useDeleteExternalMappingMutation, useGetMemsearchConfigQuery, useUpdateMemsearchConfigMutation, useGetStripeConnectStatusQuery, useStartStripeConnectOnboardingMutation, useLazyGetStripeConnectDashboardQuery, useListPaywallsQuery, useListPricesQuery, useCreatePriceMutation, useUpdatePriceMutation, useDeletePriceMutation, useGetPaywallConfigQuery, useEnablePaywallMutation, useUpdatePaywallMutation, useGetMentorMemoriesListQuery, useGetMemoryCategoriesAdminQuery, useCreateMentorMemoryMutation, useLazyGetCourseMetaDataQuery, useLazyGetCourseCompletionOutlinesQuery, useLazyGetCourseEligibilityQuery, useLazyGetEdxSSOTokenQuery, useCreateCourseEnrollmentMutation, useCreateStripeCheckoutSessionMutation, useLazyGetCourseProgressQuery, useLazyGetCourseCompletionQuery, useUpdateExamAttemptMutation, useStartExamMutation, useLazyGetExamInfoQuery, useGetAgentSkillResourcesQuery, useCreateAgentSkillResourceMutation, useUpdateAgentSkillResourceMutation, useUploadAgentSkillResourceAssetMutation, useDeleteAgentSkillResourceMutation, useGetMentorSkillAssignmentsQuery, useGetAgentSkillsQuery, useCreateMentorSkillAssignmentMutation, useUpdateMentorSkillAssignmentMutation, useDeleteMentorSkillAssignmentMutation, useCreateAgentSkillMutation, useUpdateAgentSkillMutation, useDeleteAgentSkillMutation, filterSlashSkills, useGetMentorSkillAssignmentsInfiniteQuery, MENTOR_SKILL_ASSIGNMENTS_PAGE_SIZE, useUpdateUserProjectMutation, useGetTrainingDocumentsQuery, useGetVectorDocumentsQuery, useLazyGetConnectedServiceAuthUrlQuery, useCreateUserProjectMutation, useDeleteUserProjectMutation, useGetUserProjectDetailsQuery, useEditTrainingDocumentMutation, useAddTrainingDocumentMutation, useLazyGetCredentialsQuery, useGetMentorSettingsQuery, useEditMentorMutation, useUploadLightLogoMutation, useUploadDarkLogoMutation, useUpdatePlatformInfoMutation, LOGO_ENDPOINTS, useCreateEvalDatasetMutation, useAddEvalDatasetItemsMutation, useUploadEvalDatasetCsvMutation, useDeleteEvalDatasetItemMutation, useListEvalDatasetItemsQuery, evalTotalPages, useListEvalDatasetsQuery, useGetUserProjectsQuery, useCreateSessionIdMutation, useGetMentorCategoriesQuery, useGetTenantChatPrivacyConfigQuery as useGetTenantChatPrivacyConfigQuery$1, useGetCallConfigurationsQuery, useCreateCallConfigurationMutation, useUpdateCallConfigurationMutation, chatPrivacyApiSlice, useDeleteMentorMutation, useForkMentorMutation, useGetToolsQuery, useLazyGetMCPServersQuery, useOauthFindMutation, useLazyStartOAuthFlowQuery, useCreateMCPServerMutation, usePartialUpdateMCPServerMutation, useCreateMCPServerConnectionMutation, usePatchMCPServerConnectionMutation, useGetConnectedServicesQuery, useGetMCPServersQuery, useGetMCPServerConnectionsQuery, useUpdateMCPServerMutation, useDeleteMCPServerMutation, useEditMentorJsonMutation, useDisconnectServiceMutation, useGetPromptCategoriesQuery, PRIVACY_ACTIONS, PRIVACY_ENTITY_TYPES, useGetVoicesQuery, useGetVoiceQuery, isBaseAgentMentor, useGetGradeResultQuery, useOverrideGradeResultMutation, useGetChatHistoryFilterQuery, useListGradeResultsQuery, useGetMentorGraderConfigurationQuery, useListGraderCriteriaQuery, useCreateMentorGraderConfigurationMutation, useUpdateMentorGraderConfigurationMutation, useCreateGraderCriterionMutation, useUpdateGraderCriterionMutation, useDeleteGraderCriterionMutation, useGetSpendCapStatusQuery, useGetMentorPublicSettingsQuery, useGetChatHistoryQuery, useGetMentorSummariesQuery, useGetConversationMemoriesQuery, useGetSupportTicketsQuery, useGetSupportTicketMessagesQuery, useCreateSupportTicketMessageMutation, usePatchSupportTicketMutation, useCloseSupportTicketMutation, useCreateLtiMentorMutation, useUpdateLtiMentorMutation, useGetLtiMentorsQuery, useDeleteLtiMentorMutation, useCreateLtiKeyMutation, useUpdateLtiKeyMutation, useDeleteLtiKeyMutation, useGetLtiKeysQuery, useCreateLtiToolMutation, useUpdateLtiToolMutation, useGetLtiToolsQuery, useGetPlatformInfoQuery, useCreatePromptMutation, useGetPromptsSearchQuery, useUpdatePromptMutation, useCreateRedirectTokenMutation, useGetShareableLinkQuery, useCreateShareableLinkMutation, useUpdateShareableLinkMutation, useCreateDisclaimerMutation, useUpdateDisclaimerMutation, useGetDisclaimersQuery, useUpdateRbacMentorAccessMutation, useGetRbacMentorAccessListQuery, useStartEvalRunMutation, useEvaluateEvalRunMutation, useGetEvalRunQuery, useDeleteEvalScoreMutation, useListEvalRunJudgesQuery, useCreateEvalScoreMutation, useDeleteEvalRunMutation, useExportEvalRunMutation, useListEvalRunsQuery, useLazyGetEvalRunQuery, evaluationsApiSlice, EVALUATIONS_TAGS, useGetPersonnalizedMentorsQuery, useCreateCallCredentialsMutation, useGetGuidedPromptsQuery, useUpdateChatSessionSharedMutation, useUpdateMessageFeedbackMutation, useLazyGetPromptsSearchQuery, useLazyGetGuidedPromptsQuery, useGetPublicMentorsQuery, useGetPeriodicAgentsQuery, useGetPeriodicAgentLogsListQuery, useCreatePeriodicAgentMutation, useDeletePeriodicAgentMutation, useCreateMemoryCategoryMutation, useUpdateMemoryCategoryMutation, useDeleteMemoryCategoryMutation, useDeletePromptMutation, useDeleteTrainingDocumentMutation, useGetTrainingDocumentRetrainScheduleQuery, useCreateTrainingDocumentRetrainScheduleMutation, useUpdateArtifactMutation, useLazyGetArtifactVersionQuery, useLazyListArtifactVersionsQuery, useSetCurrentVersionMutation, useListArtifactVersionsQuery, useLazyGetArtifactQuery, useLazyListArtifactsQuery, useEditSessionMutation } from '@iblai/data-layer';
6
- import { removeAuthItem, setAuthItem, isTauriEmbeddedLLMHost, selectRbacPermissions, useCurrentTenant, canViewOtherUsersChatHistory, getInitials, useTenantMetadata, isPureWatcher, WithPermissions, canWriteField, useStripeUpgrade, openExternalUrl, CHAT_AREA_SIZE, isAlphaNumeric32, checkRbacPermission, selectNumberOfActiveChatMessages, useUsername, selectStreaming, isLoggedIn, TOOLS, hasRemoteAiConfig, isSafariBrowser, useShowAttachment, useShowVoiceCall, useShowVoiceRecorder, useMentorSettings, selectShowingSharedChat, useShowFreeTrialDialog, useEmbedMode, chatInputSliceSelectors, useResponsive, useAccessingPublicRoute, useVisitingTenant, useModelFileUploadCapabilities, useChatFileUpload, useVoiceChat, selectAttachedFiles, MENTOR_CHAT_DOCUMENTS_EXTENSIONS, removeFile, chatInputSliceActions, chatActions, WithFormPermissions, useTenantContext, TimeTracker, advancedTabsProperties, defaultSessionIds, pickWritableFields, ANONYMOUS_USERNAME as ANONYMOUS_USERNAME$3, redirectToAuthSpaJoinTenant, selectActiveTab, useAxdToken, useWelcomeMessage, markdownToPlainText, useCachedSessionId, use402ErrorCheck, use401TokenRefresh, selectTokenEnabled, selectToken, useServiceWorker, useUserAgreement, useAdvancedChat, sendMessageToParentWebsite, useMentorTools, useFileDragDrop, eventBus, RemoteEvents, selectEnableChatActionsPopup, advancedTabs, isInIframe, getAuthSpaJoinUrl, addMessage, clearFiles, MENTOR_VISIBILITY } from '@iblai/web-utils';
5
+ import { useGetUserMetadataQuery, useGetUserMetadataEdxQuery, useGetUserProfileSummaryQuery, useUpdateUserMetadataMutation, useUpdateUserMetadataEdxMutation, useUploadProfileImageMutation, useResetPasswordMutation, useSelfRetireMutation, useCreateUserInstitutionMutation, useGetUserInstitutionsQuery, useCreateUserEducationMutation, useUpdateUserEducationMutation, useDeleteUserEducationMutation, useGetUserEducationQuery, useCreateUserCompanyMutation, useGetUserCompaniesQuery, useCreateUserExperienceMutation, useUpdateUserExperienceMutation, useDeleteUserExperienceMutation, useGetUserExperienceQuery, useGetUserResumeQuery, useCreateUserResumeMutation, useGetMySubscriptionsQuery, useGetItemSubscriptionQuery, useCancelSubscriptionMutation, useGetLearnerDetailsQuery, useGetUserEnrolledCoursesQuery, useGetUserEnrolledProgramsQuery, useGetUserCatalogPathwaysQuery, useGetUsersAsAssertionsQuery, useGetUserEarnedSkillsQuery, useGetUserReportedSkillsQuery, useGetUserDesiredSkillsQuery, useCreateGlobalMemoryMutation, useUpdateGlobalMemoryMutation, useUpdateMentorMemoryMutation, useGetMemsearchStatusQuery, useGetUserMemorySettingsQuery, useUpdateUserMemorySettingsMutation, useGetGlobalMemoriesQuery, useGetAllMentorMemoriesQuery, useDeleteGlobalMemoryMutation, useDeleteMentorMemoryMutation, useGetAiSearchMentorsQuery, useStarMentorMutation, useUnstarMentorMutation, useGetUserChatHistoryReportsQuery, USER_CHAT_HISTORY_REPORT_ACTIVE_STATES, useCreateUserChatHistoryReportMutation, useLazyGetDownloadReportFromURLQuery, useGetUserChatHistoryReportStatusQuery, userChatHistoryApiSlice, USER_CHAT_HISTORY_QUERY_KEYS, useGetUserChatHistoryQuery, useGetUserChatHistoryFilterQuery, useGetMentorsQuery, useGetUserChatHistorySessionQuery, useUserLlmUsageSummary, useUserLlmSpendOverTime, useUserLlmCostByModel, useUserLlmCostByService, useUserLlmRecentActivity, USER_LLM_RECENT_ACTIVITY_PAGE_SIZE, useUserLlmTraceObservations, useInviteUserMutation, usePlatformInvitationsQuery, useCreateCatalogInvitationCourseBulkMutation, useGetCatalogInvitationsCourseQuery, useLazyPlatformUsersQuery, useLazyPlatformUserGroupsQuery, useGetPersonnalizedSearchQuery, useCreateCatalogInvitationProgramBulkMutation, useGetCatalogInvitationsProgramQuery, useGetVectorDocumentsQuery, useGetLlmsQuery, useUpdateUserRoleMutation, useUpdateUserStatusMutation, useUpdatePlatformUserRoleWithPoliciesMutation, usePlatformUsersQuery, useGetAccessibleUsersQuery, isPoliciesResponse, platformApiSlice, featureTags, useLazyGetRbacTeamsAccessListQuery, useCreateRbacTeamsAccessMutation, useGetRbacGroupsQuery, usePlatformUserGroupsQuery, useCreateRbacGroupMutation, useUpdateRbacGroupMutation, useDeleteRbacGroupMutation, useCreatePlatformUserGroupMutation, useUpdatePlatformUserGroupMutation, useDeletePlatformUserGroupMutation, useGetRbacGroupDetailsQuery, useGetPlatformUserGroupDetailsQuery, useGetRbacPermissionsMutation, useGetRbacRolesQuery, useCreateRbacRoleMutation, useUpdateRbacRoleMutation, useDeleteRbacRoleMutation, useGetRbacRoleDetailsQuery, useGetRbacActionsDefinitionQuery, useGetRbacResourceDiscoveryQuery, useGetRbacResourceListQuery, useGetRbacPoliciesQuery, useCreateRbacPolicyMutation, useUpdateRbacPolicyMutation, useDeleteRbacPolicyMutation, useGetRbacPolicyDetailsQuery, useGetWatchedGroupsQuery, useCreateWatchedGroupMutation, useUpdateWatchedGroupMutation, useDeleteWatchedGroupMutation, useAddWatchedUserMutation, useRemoveWatchedUserMutation, useAddWatcherMutation, useUpdateWatcherMutation, useDeleteWatcherMutation, useGetWatchedUsersQuery, useGetWatchersQuery, WATCHER_NOTIFICATION_EVENTS, WATCHER_NOTIFICATION_EVENT_LABELS, useGetApplicationFormsQuery, useGetApplicationFormDetailQuery, useCreateApplicationFormMutation, useUpdateApplicationFormMutation, useGetApplicationAdminDetailQuery, useInviteApplicationCandidateMutation, useLazyGetApplicationAttachmentFileQuery, useCreateApplicationWaiverMutation, useDeleteApplicationWaiverMutation, useSubmitApplicationOverrideMutation, useUpdateApplicationPlacementMutation, useDeleteApplicationCourseAssignmentMutation, useCreateApplicationNoteMutation, useUpdateApplicationNoteMutation, useDeleteApplicationNoteMutation, useTransitionApplicationMutation, useLinkApplicationCandidateMutation, useCreateApplicationPlacementsMutation, useGetCustomMentorsQuery, useCreateApplicationCourseAssignmentsMutation, useGetApplicationBlocksQuery, useDeleteApplicationBlockMutation, useGetApplicationsPipelineStatsQuery, useGetApplicationsPipelineQuery, useGetTenantMetadataQuery, useUpdateTenantMetadataMutation, useLazyGetTenantMetadataQuery, useDeleteApiKeyMutation, useGetApiKeyQuery, useGetApiKeyFieldPermissionsQuery, useUpdateApiKeyMutation, useGetApiKeysQuery, unwrapApiTokenList, useCreateApiKeyMutation, useCreateLLMCredentialMutation, useGetCredentialsSchemaQuery, useGetMaskedLLMCredentialsQuery, useDeleteIntegrationCredentialMutation, useDeleteCredentialMutation, useCreateIntegrationCredentialMutation, useGetIntegrationCredentialsSchemaQuery, useGetMaskedIntegrationCredentialsQuery, useGetAccountBillingInfoQuery, useUpdateAutoRechargeInfoMutation, useTriggerAutoRechargeMutation, useGetAgentSpendCapQuery, useGetTenantSpendCapQuery, useListAgentUserSpendCapsQuery, useUpsertAgentSpendCapMutation, useDeleteAgentSpendCapMutation, useUpsertTenantSpendCapMutation, useDeleteTenantSpendCapMutation, useUpsertUserSpendCapMutation, useDeleteUserSpendCapMutation, useGetLlmUsageMetricsQuery, useListAgentSpendCapsQuery, useGetCreditTransactionsQuery, useCreateStripeCustomerPortalMutation, useSetPlatformConfigurationsMutation, useGetPlatformConfigurationsQuery, useUpdatePlatformMembershipMutation, useGetPlatformMembershipQuery, useGetCustomDomainsQuery, useCreateCustomDomainMutation, useDeleteCustomDomainMutation, useGetStudentMentorCreationStatusQuery, useSetStudentMentorCreationStatusMutation, coreApiSlice, recommendationPromptTypeEnum, useGetRecommendedPromptsListQuery, useCreateRecommendedPromptMutation, useUpdateRecommendedPromptMutation, useDeleteRecommendedPromptMutation, useLazyGetPublicPlatformImageAssetFileUrlQuery, useCreatePlatformImageAssetMutation, useGetProviderConfigQuery, useCreateProviderConfigMutation, useDeleteProviderConfigMutation, useGetExternalMappingQuery, useGetCredentialsListQuery, useCreateExternalMappingMutation, useDeleteExternalMappingMutation, useGetMemsearchConfigQuery, useUpdateMemsearchConfigMutation, useGetStripeConnectStatusQuery, useStartStripeConnectOnboardingMutation, useLazyGetStripeConnectDashboardQuery, useListPaywallsQuery, useListPricesQuery, useCreatePriceMutation, useUpdatePriceMutation, useDeletePriceMutation, useGetPaywallConfigQuery, useEnablePaywallMutation, useUpdatePaywallMutation, useGetMentorMemoriesListQuery, useGetMemoryCategoriesAdminQuery, useCreateMentorMemoryMutation, useLazyGetCourseMetaDataQuery, useLazyGetCourseCompletionOutlinesQuery, useLazyGetCourseEligibilityQuery, useLazyGetEdxSSOTokenQuery, useCreateCourseEnrollmentMutation, useCreateStripeCheckoutSessionMutation, useLazyGetCourseProgressQuery, useLazyGetCourseCompletionQuery, useUpdateExamAttemptMutation, useStartExamMutation, useLazyGetExamInfoQuery, useGetAgentSkillResourcesQuery, useCreateAgentSkillResourceMutation, useUpdateAgentSkillResourceMutation, useUploadAgentSkillResourceAssetMutation, useDeleteAgentSkillResourceMutation, useGetMentorSkillAssignmentsQuery, useGetAgentSkillsQuery, useCreateMentorSkillAssignmentMutation, useUpdateMentorSkillAssignmentMutation, useDeleteMentorSkillAssignmentMutation, useCreateAgentSkillMutation, useUpdateAgentSkillMutation, useDeleteAgentSkillMutation, filterSlashSkills, useGetMentorSkillAssignmentsInfiniteQuery, MENTOR_SKILL_ASSIGNMENTS_PAGE_SIZE, useUpdateUserProjectMutation, useGetTrainingDocumentsQuery, useLazyGetConnectedServiceAuthUrlQuery, useCreateUserProjectMutation, useDeleteUserProjectMutation, useGetUserProjectDetailsQuery, useEditTrainingDocumentMutation, useAddTrainingDocumentMutation, useLazyGetCredentialsQuery, useGetMentorSettingsQuery, useEditMentorMutation, useUploadLightLogoMutation, useUploadDarkLogoMutation, useUpdatePlatformInfoMutation, LOGO_ENDPOINTS, useCreateEvalDatasetMutation, useAddEvalDatasetItemsMutation, useUploadEvalDatasetCsvMutation, useDeleteEvalDatasetItemMutation, useListEvalDatasetItemsQuery, evalTotalPages, useListEvalDatasetsQuery, useGetUserProjectsQuery, useCreateSessionIdMutation, useGetMentorCategoriesQuery, useGetTenantChatPrivacyConfigQuery as useGetTenantChatPrivacyConfigQuery$1, useGetCallConfigurationsQuery, useCreateCallConfigurationMutation, useUpdateCallConfigurationMutation, chatPrivacyApiSlice, useDeleteMentorMutation, useForkMentorMutation, useGetToolsQuery, useLazyGetMCPServersQuery, useOauthFindMutation, useLazyStartOAuthFlowQuery, useCreateMCPServerMutation, usePartialUpdateMCPServerMutation, useCreateMCPServerConnectionMutation, usePatchMCPServerConnectionMutation, useGetConnectedServicesQuery, useGetMCPServersQuery, useGetMCPServerConnectionsQuery, useUpdateMCPServerMutation, useDeleteMCPServerMutation, useEditMentorJsonMutation, useDisconnectServiceMutation, useGetPromptCategoriesQuery, PRIVACY_ACTIONS, PRIVACY_ENTITY_TYPES, useGetVoicesQuery, useGetVoiceQuery, isBaseAgentMentor, useGetGradeResultQuery, useOverrideGradeResultMutation, useGetChatHistoryFilterQuery, useListGradeResultsQuery, useGetMentorGraderConfigurationQuery, useListGraderCriteriaQuery, useCreateMentorGraderConfigurationMutation, useUpdateMentorGraderConfigurationMutation, useCreateGraderCriterionMutation, useUpdateGraderCriterionMutation, useDeleteGraderCriterionMutation, useGetSpendCapStatusQuery, useGetMentorPublicSettingsQuery, useGetChatHistoryQuery, useGetMentorSummariesQuery, useGetConversationMemoriesQuery, useGetSupportTicketsQuery, useGetSupportTicketMessagesQuery, useCreateSupportTicketMessageMutation, usePatchSupportTicketMutation, useCloseSupportTicketMutation, useCreateLtiMentorMutation, useUpdateLtiMentorMutation, useGetLtiMentorsQuery, useDeleteLtiMentorMutation, useCreateLtiKeyMutation, useUpdateLtiKeyMutation, useDeleteLtiKeyMutation, useGetLtiKeysQuery, useCreateLtiToolMutation, useUpdateLtiToolMutation, useGetLtiToolsQuery, useGetPlatformInfoQuery, useCreatePromptMutation, useGetPromptsSearchQuery, useUpdatePromptMutation, useCreateRedirectTokenMutation, useGetShareableLinkQuery, useCreateShareableLinkMutation, useUpdateShareableLinkMutation, useCreateDisclaimerMutation, useUpdateDisclaimerMutation, useGetDisclaimersQuery, useUpdateRbacMentorAccessMutation, useGetRbacMentorAccessListQuery, useStartEvalRunMutation, useEvaluateEvalRunMutation, useGetEvalRunQuery, useDeleteEvalScoreMutation, useListEvalRunJudgesQuery, useCreateEvalScoreMutation, useDeleteEvalRunMutation, useExportEvalRunMutation, useListEvalRunsQuery, useLazyGetEvalRunQuery, evaluationsApiSlice, EVALUATIONS_TAGS, useGetPersonnalizedMentorsQuery, useCreateCallCredentialsMutation, useGetGuidedPromptsQuery, useUpdateChatSessionSharedMutation, useUpdateMessageFeedbackMutation, useLazyGetPromptsSearchQuery, useLazyGetGuidedPromptsQuery, useGetPublicMentorsQuery, useGetPeriodicAgentsQuery, useGetPeriodicAgentLogsListQuery, useCreatePeriodicAgentMutation, useDeletePeriodicAgentMutation, useCreateMemoryCategoryMutation, useUpdateMemoryCategoryMutation, useDeleteMemoryCategoryMutation, useDeletePromptMutation, useDeleteTrainingDocumentMutation, useGetTrainingDocumentRetrainScheduleQuery, useCreateTrainingDocumentRetrainScheduleMutation, useUpdateArtifactMutation, useLazyGetArtifactVersionQuery, useLazyListArtifactVersionsQuery, useSetCurrentVersionMutation, useListArtifactVersionsQuery, useLazyGetArtifactQuery, useLazyListArtifactsQuery, useEditSessionMutation } from '@iblai/data-layer';
6
+ import { removeAuthItem, setAuthItem, isTauriEmbeddedLLMHost, selectRbacPermissions, useCurrentTenant, canViewOtherUsersChatHistory, isPureWatcher, getInitials, useTenantMetadata, useUsername, selectStreaming, isLoggedIn, TOOL_NAME_MAP, WRITE_TODOS_TOOL, WithPermissions, canWriteField, useStripeUpgrade, openExternalUrl, CHAT_AREA_SIZE, isAlphaNumeric32, checkRbacPermission, selectNumberOfActiveChatMessages, TOOLS, hasRemoteAiConfig, isSafariBrowser, useShowAttachment, useShowVoiceCall, useShowVoiceRecorder, useMentorSettings, selectShowingSharedChat, useShowFreeTrialDialog, useEmbedMode, chatInputSliceSelectors, useResponsive, useAccessingPublicRoute, useVisitingTenant, useModelFileUploadCapabilities, useChatFileUpload, useVoiceChat, selectAttachedFiles, MENTOR_CHAT_DOCUMENTS_EXTENSIONS, removeFile, chatInputSliceActions, chatActions, WithFormPermissions, useTenantContext, TimeTracker, advancedTabsProperties, defaultSessionIds, pickWritableFields, ANONYMOUS_USERNAME as ANONYMOUS_USERNAME$3, redirectToAuthSpaJoinTenant, selectActiveTab, useAxdToken, useWelcomeMessage, markdownToPlainText, useCachedSessionId, use402ErrorCheck, use401TokenRefresh, selectTokenEnabled, selectToken, useServiceWorker, useUserAgreement, useAdvancedChat, sendMessageToParentWebsite, useMentorTools, useFileDragDrop, eventBus, RemoteEvents, selectEnableChatActionsPopup, advancedTabs, isInIframe, getAuthSpaJoinUrl, addMessage, clearFiles, MENTOR_VISIBILITY } from '@iblai/web-utils';
7
7
  import { useDispatch, useSelector } from 'react-redux';
8
8
  import { toast } from 'sonner';
9
9
  import { useSearchParams, useParams, useRouter, usePathname } from 'next/navigation';
@@ -44326,7 +44326,7 @@ var isArray$h = function isArray(arr) {
44326
44326
  return toStr.call(arr) === '[object Array]';
44327
44327
  };
44328
44328
 
44329
- var isPlainObject$4 = function isPlainObject(obj) {
44329
+ var isPlainObject$5 = function isPlainObject(obj) {
44330
44330
  if (!obj || toStr.call(obj) !== '[object Object]') {
44331
44331
  return false;
44332
44332
  }
@@ -44405,12 +44405,12 @@ var extend$3 = function extend() {
44405
44405
  // Prevent never-ending loop
44406
44406
  if (target !== copy) {
44407
44407
  // Recurse if we're merging plain objects or arrays
44408
- if (deep && copy && (isPlainObject$4(copy) || (copyIsArray = isArray$h(copy)))) {
44408
+ if (deep && copy && (isPlainObject$5(copy) || (copyIsArray = isArray$h(copy)))) {
44409
44409
  if (copyIsArray) {
44410
44410
  copyIsArray = false;
44411
44411
  clone = src && isArray$h(src) ? src : [];
44412
44412
  } else {
44413
- clone = src && isPlainObject$4(src) ? src : {};
44413
+ clone = src && isPlainObject$5(src) ? src : {};
44414
44414
  }
44415
44415
 
44416
44416
  // Never move original objects, clone them
@@ -44431,7 +44431,7 @@ var extend$3 = function extend() {
44431
44431
 
44432
44432
  var extend$2 = /*@__PURE__*/getDefaultExportFromCjs(extend$3);
44433
44433
 
44434
- function isPlainObject$3(value) {
44434
+ function isPlainObject$4(value) {
44435
44435
  if (typeof value !== 'object' || value === null) {
44436
44436
  return false;
44437
44437
  }
@@ -45502,7 +45502,7 @@ class Processor extends CallableInstance {
45502
45502
  else if (parameters.length > 0) {
45503
45503
  let [primary, ...rest] = parameters;
45504
45504
  const currentPrimary = attachers[entryIndex][1];
45505
- if (isPlainObject$3(currentPrimary) && isPlainObject$3(primary)) {
45505
+ if (isPlainObject$4(currentPrimary) && isPlainObject$4(primary)) {
45506
45506
  primary = extend$2(true, currentPrimary, primary);
45507
45507
  }
45508
45508
 
@@ -45594,7 +45594,7 @@ function assertUnfrozen(name, frozen) {
45594
45594
  function assertNode(node) {
45595
45595
  // `isPlainObj` unfortunately uses `any` instead of `unknown`.
45596
45596
  // type-coverage:ignore-next-line
45597
- if (!isPlainObject$3(node) || typeof node.type !== 'string') {
45597
+ if (!isPlainObject$4(node) || typeof node.type !== 'string') {
45598
45598
  throw new TypeError('Expected node, got `' + node + '`')
45599
45599
  // Fine.
45600
45600
  }
@@ -97047,7 +97047,7 @@ var analyticsAnalyticsTranscriptsStats$3 = {
97047
97047
  labelUsername: "Username:",
97048
97048
  labelAgent: "Agent:",
97049
97049
  labelModel: "Model:",
97050
- labelModelCard: "model:",
97050
+ labelModelCard: "Model:",
97051
97051
  labelUser: "User:",
97052
97052
  roleUser: "User",
97053
97053
  roleAgent: "Agent",
@@ -97089,6 +97089,37 @@ var analyticsAnalyticsTranscriptsStats$3 = {
97089
97089
  avgCostPerConversationDescription: "What a typical conversation costs in model usage.",
97090
97090
  avgRatingDescription: "Average rating users gave their conversations, on a scale of 1 to 5."
97091
97091
  };
97092
+ var analyticsTranscriptTurnDetails$3 = {
97093
+ showDetails: "Show Details",
97094
+ hideDetails: "Hide Details",
97095
+ metadata: "Metadata",
97096
+ requestContext: "Request Context",
97097
+ model: "Model",
97098
+ provider: "Provider",
97099
+ temperature: "Temperature",
97100
+ client: "Client",
97101
+ sourcesBadge: "Documents · {count}",
97102
+ toolsBadge: "Tools · {count}",
97103
+ yes: "Yes",
97104
+ no: "No",
97105
+ redacted: "Redacted",
97106
+ fieldSession: "Session",
97107
+ fieldCallerVerified: "Caller Verified",
97108
+ fieldFiles: "Files",
97109
+ fieldFile: "File",
97110
+ fieldSystemPrompt: "System Prompt",
97111
+ fieldFlow: "Flow",
97112
+ fieldToken: "Token",
97113
+ fieldTenant: "Tenant",
97114
+ fieldPathway: "Pathway",
97115
+ fieldUsername: "Username",
97116
+ fieldName: "Name",
97117
+ fieldCanvas: "Canvas",
97118
+ fieldProactivePrompt: "Proactive Prompt",
97119
+ fieldGrader: "Grader",
97120
+ fieldDatasets: "Datasets",
97121
+ fieldDateTime: "Date & Time"
97122
+ };
97092
97123
  var analyticsAnalyticsUsersStats$3 = {
97093
97124
  usersLoggedInNow: "Users logged in right now",
97094
97125
  usersLoggedInPast30Days: "Users logged in past 30 days",
@@ -98579,7 +98610,8 @@ var historyTabLabels$3 = {
98579
98610
  noResponse: "No response available",
98580
98611
  selectPrompt: "Select a conversation to view details.",
98581
98612
  aiAgent: "AI Agent",
98582
- noConversations: "No conversations found"
98613
+ noConversations: "No conversations found",
98614
+ documentsChip: "Documents · {count}"
98583
98615
  },
98584
98616
  summary: {
98585
98617
  notAvailable: "Summary not available"
@@ -99704,7 +99736,13 @@ var profileAccount$3 = {
99704
99736
  tabBenchmarks: "Benchmarks",
99705
99737
  descBenchmarks: "Build benchmark suites of test inputs to evaluate agents.",
99706
99738
  tabMemory: "Memory",
99707
- descMemory: "Manage user global memories and agent memories."
99739
+ descMemory: "Manage user global memories and agent memories.",
99740
+ groupWorkspace: "Workspace",
99741
+ groupAiData: "AI & data",
99742
+ groupSystem: "System",
99743
+ ariaSettingsNav: "Organization settings",
99744
+ ariaSettingsNavMobile: "Organization settings (mobile)",
99745
+ groupFinance: "Finance"
99708
99746
  };
99709
99747
  var profileAddCreditsModal$3 = {
99710
99748
  title: "Add Credits",
@@ -99900,7 +99938,6 @@ var profileIndex$3 = {
99900
99938
  langSpanish: "Spanish",
99901
99939
  langFrench: "French",
99902
99940
  checkboxDisplayMentorAI: "Display Agent Sidebar",
99903
- checkboxDisplayLeaderboard: "Display Leaderboard",
99904
99941
  labelFacebook: "Facebook",
99905
99942
  placeholderFacebook: "Facebook",
99906
99943
  labelLinkedIn: "LinkedIn",
@@ -99921,7 +99958,16 @@ var profileIndex$3 = {
99921
99958
  descChatHistoryPreview: "Conversations with AI agents.",
99922
99959
  ariaAddEducation: "Add education",
99923
99960
  ariaAddExperience: "Add experience",
99924
- ariaAddResume: "Add resume"
99961
+ ariaAddResume: "Add resume",
99962
+ tabLlmUsage: "Usage",
99963
+ descLlmUsage: "See how much AI you have used: spend, tokens and requests across your conversations.",
99964
+ groupProfile: "Profile",
99965
+ groupAiData: "AI & data",
99966
+ groupAccount: "Account",
99967
+ groupRecords: "Records",
99968
+ descLlmUsagePreview: "Spend, tokens and requests across conversations.",
99969
+ sectionFeatures: "Features",
99970
+ tooltipDisplayMentorAI: "Keeps the AI agent panel beside whatever you are working on, so you can ask questions without leaving the page."
99925
99971
  };
99926
99972
  var profileInviteUserContent$3 = {
99927
99973
  inviteUsersTitle: "Invite Users",
@@ -102946,6 +102992,81 @@ var analyticsTraces$3 = {
102946
102992
  mentorScopeTitle: "Agent scope narrows the trace list only.",
102947
102993
  mentorScopeBody: "Row-level observations cannot be filtered to a single agent upstream — open a trace to see its own calls."
102948
102994
  };
102995
+ var profileLlmUsage$3 = {
102996
+ rangeLabel: "Date range",
102997
+ rangeToday: "Today",
102998
+ range7d: "7 days",
102999
+ range30d: "30 days",
103000
+ range90d: "90 days",
103001
+ rangeAllTime: "All time",
103002
+ periodToday: "Today",
103003
+ period7d: "Last 7 days",
103004
+ period30d: "Last 30 days",
103005
+ period90d: "Last 90 days",
103006
+ periodAllTime: "All time",
103007
+ scopeSelf: "Only your own activity is shown",
103008
+ refresh: "Refresh usage",
103009
+ kpiSpend: "Spend",
103010
+ kpiTokens: "Tokens",
103011
+ kpiRequests: "Requests",
103012
+ kpiAvgCost: "Avg cost per request",
103013
+ kpiTokensSub: "{value} per request",
103014
+ kpiRequestsSub: "AI calls",
103015
+ kpiAvgCostSub: "Spend divided by requests",
103016
+ chartTitle: "Spend over time",
103017
+ chartSubtitleDay: "Daily spend in US dollars",
103018
+ chartSubtitleWeek: "Weekly spend in US dollars",
103019
+ chartSubtitleMonth: "Monthly spend in US dollars",
103020
+ chartAria: "Spend over time. Use the left and right arrow keys to read each bar.",
103021
+ chartNoData: "No spend in this period.",
103022
+ chartTableCaption: "Spend per period",
103023
+ colPeriod: "Period",
103024
+ colCost: "Cost",
103025
+ byModelTitle: "By model",
103026
+ byModelSubtitle: "Share of your spend per model",
103027
+ byServiceTitle: "By service",
103028
+ byServiceSubtitle: "Where your spend comes from",
103029
+ otherRow: "Other",
103030
+ unknownModel: "Unknown model",
103031
+ unknownService: "Other service",
103032
+ breakdownEmpty: "Nothing to break down in this period.",
103033
+ sectionError: "This section couldn't be loaded.",
103034
+ activityTitle: "Recent activity",
103035
+ activitySubtitle: "Your latest AI requests, newest first. Open one to see each step.",
103036
+ activityEmpty: "No requests in this period.",
103037
+ colWhen: "When",
103038
+ colService: "Service",
103039
+ colSession: "Session",
103040
+ colLatency: "Latency",
103041
+ colDetails: "Details",
103042
+ showSteps: "Show steps for {when}",
103043
+ hideSteps: "Hide steps for {when}",
103044
+ stepsLoading: "Loading steps…",
103045
+ stepsEmpty: "No steps were recorded for this request.",
103046
+ stepsError: "The steps for this request couldn't be loaded.",
103047
+ colStep: "Step",
103048
+ colModel: "Model",
103049
+ colInput: "Input",
103050
+ colOutput: "Output",
103051
+ colTotal: "Total",
103052
+ paginationSummary: "Showing {from}–{to} of {total}",
103053
+ previous: "Previous",
103054
+ next: "Next",
103055
+ emptyTitle: "No AI usage in this period",
103056
+ emptyBody: "Chats, memory and other AI requests show up here once they happen. Try a longer range.",
103057
+ emptyAction90: "Show last 90 days",
103058
+ emptyActionAll: "Show all time",
103059
+ errorForbiddenTitle: "Usage isn't available",
103060
+ errorForbiddenBody: "We couldn't load usage for this account.",
103061
+ errorUnavailableTitle: "Usage is temporarily unavailable",
103062
+ errorUnavailableBody: "Please try again in a few minutes.",
103063
+ errorUnauthorizedTitle: "Your session has expired",
103064
+ errorUnauthorizedBody: "Sign in again to see your usage.",
103065
+ errorUnknownTitle: "Something went wrong",
103066
+ errorUnknownBody: "We couldn't load your usage.",
103067
+ tryAgain: "Try again",
103068
+ scopeUser: "Usage for {username}"
103069
+ };
102949
103070
  var en = {
102950
103071
  accessTabAddAccess: accessTabAddAccess$3,
102951
103072
  accessTabLabels: accessTabLabels$3,
@@ -102982,6 +103103,7 @@ var en = {
102982
103103
  analyticsAnalyticsReports: analyticsAnalyticsReports$3,
102983
103104
  analyticsAnalyticsTopicsStats: analyticsAnalyticsTopicsStats$3,
102984
103105
  analyticsAnalyticsTranscriptsStats: analyticsAnalyticsTranscriptsStats$3,
103106
+ analyticsTranscriptTurnDetails: analyticsTranscriptTurnDetails$3,
102985
103107
  analyticsAnalyticsUsersStats: analyticsAnalyticsUsersStats$3,
102986
103108
  analyticsGroupsFilterDropdown: analyticsGroupsFilterDropdown$3,
102987
103109
  analyticsStatCard: analyticsStatCard$3,
@@ -103229,7 +103351,8 @@ var en = {
103229
103351
  analyticsChartCard: analyticsChartCard$3,
103230
103352
  analyticsCostTabs: analyticsCostTabs$3,
103231
103353
  analyticsLlmUsage: analyticsLlmUsage$3,
103232
- analyticsTraces: analyticsTraces$3
103354
+ analyticsTraces: analyticsTraces$3,
103355
+ profileLlmUsage: profileLlmUsage$3
103233
103356
  };
103234
103357
 
103235
103358
  var accessTabAddAccess$2 = {
@@ -104205,7 +104328,7 @@ var analyticsAnalyticsTranscriptsStats$2 = {
104205
104328
  labelUsername: "Nom d'utilisateur :",
104206
104329
  labelAgent: "Agent :",
104207
104330
  labelModel: "Modèle :",
104208
- labelModelCard: "modèle :",
104331
+ labelModelCard: "Modèle :",
104209
104332
  labelUser: "Utilisateur :",
104210
104333
  roleUser: "Utilisateur",
104211
104334
  roleAgent: "Agent",
@@ -104247,6 +104370,37 @@ var analyticsAnalyticsTranscriptsStats$2 = {
104247
104370
  avgCostPerConversationDescription: "Coût d’utilisation des modèles pour une conversation type.",
104248
104371
  avgRatingDescription: "Note moyenne attribuée par les utilisateurs à leurs conversations, sur une échelle de 1 à 5."
104249
104372
  };
104373
+ var analyticsTranscriptTurnDetails$2 = {
104374
+ showDetails: "Afficher les détails",
104375
+ hideDetails: "Masquer les détails",
104376
+ metadata: "Métadonnées",
104377
+ requestContext: "Contexte de la requête",
104378
+ model: "Modèle",
104379
+ provider: "Fournisseur",
104380
+ temperature: "Température",
104381
+ client: "Client",
104382
+ sourcesBadge: "Documents · {count}",
104383
+ toolsBadge: "Outils · {count}",
104384
+ yes: "Oui",
104385
+ no: "Non",
104386
+ redacted: "Masqué",
104387
+ fieldSession: "Session",
104388
+ fieldCallerVerified: "Appelant vérifié",
104389
+ fieldFiles: "Fichiers",
104390
+ fieldFile: "Fichier",
104391
+ fieldSystemPrompt: "Prompt système",
104392
+ fieldFlow: "Flux",
104393
+ fieldToken: "Jeton",
104394
+ fieldTenant: "Locataire",
104395
+ fieldPathway: "Parcours",
104396
+ fieldUsername: "Nom d’utilisateur",
104397
+ fieldName: "Nom",
104398
+ fieldCanvas: "Canvas",
104399
+ fieldProactivePrompt: "Prompt proactif",
104400
+ fieldGrader: "Correcteur",
104401
+ fieldDatasets: "Jeux de données",
104402
+ fieldDateTime: "Date et heure"
104403
+ };
104250
104404
  var analyticsAnalyticsUsersStats$2 = {
104251
104405
  usersLoggedInNow: "Utilisateurs connectés en ce moment",
104252
104406
  usersLoggedInPast30Days: "Utilisateurs connectés ces 30 derniers jours",
@@ -105903,7 +106057,8 @@ var historyTabLabels$2 = {
105903
106057
  noResponse: "Aucune réponse disponible",
105904
106058
  selectPrompt: "Sélectionnez une conversation pour afficher les détails.",
105905
106059
  aiAgent: "AI Agent",
105906
- noConversations: "Aucune conversation trouvée"
106060
+ noConversations: "Aucune conversation trouvée",
106061
+ documentsChip: "Documents · {count}"
105907
106062
  },
105908
106063
  summary: {
105909
106064
  notAvailable: "Résumé non disponible"
@@ -107028,7 +107183,13 @@ var profileAccount$2 = {
107028
107183
  tabBenchmarks: "Benchmarks",
107029
107184
  descBenchmarks: "Créez des suites de benchmarks d'entrées de test pour évaluer les agents.",
107030
107185
  tabMemory: "Mémoire",
107031
- descMemory: "Gérez les mémoires globales des utilisateurs et les mémoires des agents."
107186
+ descMemory: "Gérez les mémoires globales des utilisateurs et les mémoires des agents.",
107187
+ groupWorkspace: "Espace de travail",
107188
+ groupAiData: "IA et données",
107189
+ groupSystem: "Système",
107190
+ ariaSettingsNav: "Paramètres de l'organisation",
107191
+ ariaSettingsNavMobile: "Paramètres de l'organisation (mobile)",
107192
+ groupFinance: "Finance"
107032
107193
  };
107033
107194
  var profileAddCreditsModal$2 = {
107034
107195
  title: "Ajouter des crédits",
@@ -107224,7 +107385,6 @@ var profileIndex$2 = {
107224
107385
  langSpanish: "Espagnol",
107225
107386
  langFrench: "Français",
107226
107387
  checkboxDisplayMentorAI: "Afficher la barre latérale de l'agent",
107227
- checkboxDisplayLeaderboard: "Afficher le classement",
107228
107388
  labelFacebook: "Facebook",
107229
107389
  placeholderFacebook: "Facebook",
107230
107390
  labelLinkedIn: "LinkedIn",
@@ -107245,7 +107405,16 @@ var profileIndex$2 = {
107245
107405
  descChatHistoryPreview: "Conversations avec les agents IA.",
107246
107406
  ariaAddEducation: "Ajouter une formation",
107247
107407
  ariaAddExperience: "Ajouter une expérience",
107248
- ariaAddResume: "Ajouter un CV"
107408
+ ariaAddResume: "Ajouter un CV",
107409
+ tabLlmUsage: "Utilisation",
107410
+ descLlmUsage: "Voyez combien d'IA vous avez utilisé : dépenses, jetons et requêtes dans vos conversations.",
107411
+ groupProfile: "Profil",
107412
+ groupAiData: "IA et données",
107413
+ groupAccount: "Compte",
107414
+ groupRecords: "Dossiers",
107415
+ descLlmUsagePreview: "Dépenses, jetons et requêtes dans les conversations.",
107416
+ sectionFeatures: "Fonctionnalités",
107417
+ tooltipDisplayMentorAI: "Garde le panneau de l'agent IA à côté de ce que vous faites, pour poser des questions sans quitter la page."
107249
107418
  };
107250
107419
  var profileInviteUserContent$2 = {
107251
107420
  inviteUsersTitle: "Inviter des utilisateurs",
@@ -110104,6 +110273,81 @@ var analyticsTraces$2 = {
110104
110273
  mentorScopeTitle: "La portée par agent ne restreint que la liste des traces.",
110105
110274
  mentorScopeBody: "Les observations au niveau ligne ne peuvent pas être filtrées sur un seul agent — ouvrez une trace pour voir ses propres appels."
110106
110275
  };
110276
+ var profileLlmUsage$2 = {
110277
+ rangeLabel: "Période",
110278
+ rangeToday: "Aujourd'hui",
110279
+ range7d: "7 jours",
110280
+ range30d: "30 jours",
110281
+ range90d: "90 jours",
110282
+ rangeAllTime: "Tout",
110283
+ periodToday: "Aujourd'hui",
110284
+ period7d: "7 derniers jours",
110285
+ period30d: "30 derniers jours",
110286
+ period90d: "90 derniers jours",
110287
+ periodAllTime: "Depuis le début",
110288
+ scopeSelf: "Seule votre propre activité est affichée",
110289
+ refresh: "Actualiser l'utilisation",
110290
+ kpiSpend: "Dépenses",
110291
+ kpiTokens: "Jetons",
110292
+ kpiRequests: "Requêtes",
110293
+ kpiAvgCost: "Coût moyen par requête",
110294
+ kpiTokensSub: "{value} par requête",
110295
+ kpiRequestsSub: "Appels à l'IA",
110296
+ kpiAvgCostSub: "Dépenses divisées par les requêtes",
110297
+ chartTitle: "Dépenses dans le temps",
110298
+ chartSubtitleDay: "Dépenses quotidiennes en dollars américains",
110299
+ chartSubtitleWeek: "Dépenses hebdomadaires en dollars américains",
110300
+ chartSubtitleMonth: "Dépenses mensuelles en dollars américains",
110301
+ chartAria: "Dépenses dans le temps. Utilisez les flèches gauche et droite pour lire chaque barre.",
110302
+ chartNoData: "Aucune dépense sur cette période.",
110303
+ chartTableCaption: "Dépenses par période",
110304
+ colPeriod: "Période",
110305
+ colCost: "Coût",
110306
+ byModelTitle: "Par modèle",
110307
+ byModelSubtitle: "Part de vos dépenses par modèle",
110308
+ byServiceTitle: "Par service",
110309
+ byServiceSubtitle: "D'où viennent vos dépenses",
110310
+ otherRow: "Autres",
110311
+ unknownModel: "Modèle inconnu",
110312
+ unknownService: "Autre service",
110313
+ breakdownEmpty: "Rien à détailler sur cette période.",
110314
+ sectionError: "Cette section n'a pas pu être chargée.",
110315
+ activityTitle: "Activité récente",
110316
+ activitySubtitle: "Vos dernières requêtes d'IA, les plus récentes d'abord. Ouvrez-en une pour voir chaque étape.",
110317
+ activityEmpty: "Aucune requête sur cette période.",
110318
+ colWhen: "Quand",
110319
+ colService: "Service",
110320
+ colSession: "Session",
110321
+ colLatency: "Latence",
110322
+ colDetails: "Détails",
110323
+ showSteps: "Afficher les étapes du {when}",
110324
+ hideSteps: "Masquer les étapes du {when}",
110325
+ stepsLoading: "Chargement des étapes…",
110326
+ stepsEmpty: "Aucune étape n'a été enregistrée pour cette requête.",
110327
+ stepsError: "Les étapes de cette requête n'ont pas pu être chargées.",
110328
+ colStep: "Étape",
110329
+ colModel: "Modèle",
110330
+ colInput: "Entrée",
110331
+ colOutput: "Sortie",
110332
+ colTotal: "Total",
110333
+ paginationSummary: "{from}–{to} sur {total}",
110334
+ previous: "Précédent",
110335
+ next: "Suivant",
110336
+ emptyTitle: "Aucune utilisation de l'IA sur cette période",
110337
+ emptyBody: "Les discussions, la mémoire et les autres requêtes d'IA apparaissent ici dès qu'elles ont lieu. Essayez une période plus longue.",
110338
+ emptyAction90: "Afficher les 90 derniers jours",
110339
+ emptyActionAll: "Tout afficher",
110340
+ errorForbiddenTitle: "L'utilisation n'est pas disponible",
110341
+ errorForbiddenBody: "Nous n'avons pas pu charger l'utilisation de ce compte.",
110342
+ errorUnavailableTitle: "L'utilisation est temporairement indisponible",
110343
+ errorUnavailableBody: "Veuillez réessayer dans quelques minutes.",
110344
+ errorUnauthorizedTitle: "Votre session a expiré",
110345
+ errorUnauthorizedBody: "Reconnectez-vous pour voir votre utilisation.",
110346
+ errorUnknownTitle: "Un problème est survenu",
110347
+ errorUnknownBody: "Nous n'avons pas pu charger votre utilisation.",
110348
+ tryAgain: "Réessayer",
110349
+ scopeUser: "Utilisation de {username}"
110350
+ };
110107
110351
  var fr = {
110108
110352
  accessTabAddAccess: accessTabAddAccess$2,
110109
110353
  accessTabLabels: accessTabLabels$2,
@@ -110140,6 +110384,7 @@ var fr = {
110140
110384
  analyticsAnalyticsReports: analyticsAnalyticsReports$2,
110141
110385
  analyticsAnalyticsTopicsStats: analyticsAnalyticsTopicsStats$2,
110142
110386
  analyticsAnalyticsTranscriptsStats: analyticsAnalyticsTranscriptsStats$2,
110387
+ analyticsTranscriptTurnDetails: analyticsTranscriptTurnDetails$2,
110143
110388
  analyticsAnalyticsUsersStats: analyticsAnalyticsUsersStats$2,
110144
110389
  analyticsGroupsFilterDropdown: analyticsGroupsFilterDropdown$2,
110145
110390
  analyticsStatCard: analyticsStatCard$2,
@@ -110387,7 +110632,8 @@ var fr = {
110387
110632
  analyticsChartCard: analyticsChartCard$2,
110388
110633
  analyticsCostTabs: analyticsCostTabs$2,
110389
110634
  analyticsLlmUsage: analyticsLlmUsage$2,
110390
- analyticsTraces: analyticsTraces$2
110635
+ analyticsTraces: analyticsTraces$2,
110636
+ profileLlmUsage: profileLlmUsage$2
110391
110637
  };
110392
110638
 
110393
110639
  var accessTabAddAccess$1 = {
@@ -111363,7 +111609,7 @@ var analyticsAnalyticsTranscriptsStats$1 = {
111363
111609
  labelUsername: "Nombre de usuario:",
111364
111610
  labelAgent: "Agente:",
111365
111611
  labelModel: "Modelo:",
111366
- labelModelCard: "modelo:",
111612
+ labelModelCard: "Modelo:",
111367
111613
  labelUser: "Usuario:",
111368
111614
  roleUser: "Usuario",
111369
111615
  roleAgent: "Agente",
@@ -111405,6 +111651,37 @@ var analyticsAnalyticsTranscriptsStats$1 = {
111405
111651
  avgCostPerConversationDescription: "Lo que cuesta una conversación típica en uso de modelos.",
111406
111652
  avgRatingDescription: "Valoración media que los usuarios dieron a sus conversaciones, en una escala de 1 a 5."
111407
111653
  };
111654
+ var analyticsTranscriptTurnDetails$1 = {
111655
+ showDetails: "Mostrar detalles",
111656
+ hideDetails: "Ocultar detalles",
111657
+ metadata: "Metadatos",
111658
+ requestContext: "Contexto de la solicitud",
111659
+ model: "Modelo",
111660
+ provider: "Proveedor",
111661
+ temperature: "Temperatura",
111662
+ client: "Cliente",
111663
+ sourcesBadge: "Documentos · {count}",
111664
+ toolsBadge: "Herramientas · {count}",
111665
+ yes: "Sí",
111666
+ no: "No",
111667
+ redacted: "Oculto",
111668
+ fieldSession: "Sesión",
111669
+ fieldCallerVerified: "Llamante verificado",
111670
+ fieldFiles: "Archivos",
111671
+ fieldFile: "Archivo",
111672
+ fieldSystemPrompt: "Prompt del sistema",
111673
+ fieldFlow: "Flujo",
111674
+ fieldToken: "Token",
111675
+ fieldTenant: "Inquilino",
111676
+ fieldPathway: "Ruta",
111677
+ fieldUsername: "Nombre de usuario",
111678
+ fieldName: "Nombre",
111679
+ fieldCanvas: "Canvas",
111680
+ fieldProactivePrompt: "Prompt proactivo",
111681
+ fieldGrader: "Calificador",
111682
+ fieldDatasets: "Conjuntos de datos",
111683
+ fieldDateTime: "Fecha y hora"
111684
+ };
111408
111685
  var analyticsAnalyticsUsersStats$1 = {
111409
111686
  usersLoggedInNow: "Usuarios conectados ahora mismo",
111410
111687
  usersLoggedInPast30Days: "Usuarios conectados en los últimos 30 días",
@@ -113061,7 +113338,8 @@ var historyTabLabels$1 = {
113061
113338
  noResponse: "No hay respuesta disponible",
113062
113339
  selectPrompt: "Selecciona una conversación para ver los detalles.",
113063
113340
  aiAgent: "AI Agent",
113064
- noConversations: "No se encontraron conversaciones"
113341
+ noConversations: "No se encontraron conversaciones",
113342
+ documentsChip: "Documentos · {count}"
113065
113343
  },
113066
113344
  summary: {
113067
113345
  notAvailable: "Resumen no disponible"
@@ -114186,7 +114464,13 @@ var profileAccount$1 = {
114186
114464
  tabBenchmarks: "Benchmarks",
114187
114465
  descBenchmarks: "Cree suites de benchmarks con entradas de prueba para evaluar agentes.",
114188
114466
  tabMemory: "Memoria",
114189
- descMemory: "Gestiona las memorias globales de los usuarios y las memorias de los agentes."
114467
+ descMemory: "Gestiona las memorias globales de los usuarios y las memorias de los agentes.",
114468
+ groupWorkspace: "Espacio de trabajo",
114469
+ groupAiData: "IA y datos",
114470
+ groupSystem: "Sistema",
114471
+ ariaSettingsNav: "Configuración de la organización",
114472
+ ariaSettingsNavMobile: "Configuración de la organización (móvil)",
114473
+ groupFinance: "Finanzas"
114190
114474
  };
114191
114475
  var profileAddCreditsModal$1 = {
114192
114476
  title: "Agregar créditos",
@@ -114382,7 +114666,6 @@ var profileIndex$1 = {
114382
114666
  langSpanish: "Español",
114383
114667
  langFrench: "Francés",
114384
114668
  checkboxDisplayMentorAI: "Mostrar barra lateral del agente",
114385
- checkboxDisplayLeaderboard: "Mostrar tabla de clasificación",
114386
114669
  labelFacebook: "Facebook",
114387
114670
  placeholderFacebook: "Facebook",
114388
114671
  labelLinkedIn: "LinkedIn",
@@ -114403,7 +114686,16 @@ var profileIndex$1 = {
114403
114686
  descChatHistoryPreview: "Conversaciones con agentes de IA.",
114404
114687
  ariaAddEducation: "Añadir educación",
114405
114688
  ariaAddExperience: "Añadir experiencia",
114406
- ariaAddResume: "Añadir currículum"
114689
+ ariaAddResume: "Añadir currículum",
114690
+ tabLlmUsage: "Uso",
114691
+ descLlmUsage: "Consulta cuánta IA has usado: gasto, tokens y solicitudes en tus conversaciones.",
114692
+ groupProfile: "Perfil",
114693
+ groupAiData: "IA y datos",
114694
+ groupAccount: "Cuenta",
114695
+ groupRecords: "Registros",
114696
+ descLlmUsagePreview: "Gasto, tokens y solicitudes en las conversaciones.",
114697
+ sectionFeatures: "Funciones",
114698
+ tooltipDisplayMentorAI: "Mantiene el panel del agente de IA junto a lo que estés haciendo, para que puedas preguntar sin salir de la página."
114407
114699
  };
114408
114700
  var profileInviteUserContent$1 = {
114409
114701
  inviteUsersTitle: "Invitar usuarios",
@@ -117262,6 +117554,81 @@ var analyticsTraces$1 = {
117262
117554
  mentorScopeTitle: "El alcance por agente solo acota la lista de trazas.",
117263
117555
  mentorScopeBody: "Las observaciones a nivel de fila no pueden filtrarse por un solo agente — abre una traza para ver sus propias llamadas."
117264
117556
  };
117557
+ var profileLlmUsage$1 = {
117558
+ rangeLabel: "Rango de fechas",
117559
+ rangeToday: "Hoy",
117560
+ range7d: "7 días",
117561
+ range30d: "30 días",
117562
+ range90d: "90 días",
117563
+ rangeAllTime: "Todo",
117564
+ periodToday: "Hoy",
117565
+ period7d: "Últimos 7 días",
117566
+ period30d: "Últimos 30 días",
117567
+ period90d: "Últimos 90 días",
117568
+ periodAllTime: "Todo el tiempo",
117569
+ scopeSelf: "Solo se muestra tu propia actividad",
117570
+ refresh: "Actualizar uso",
117571
+ kpiSpend: "Gasto",
117572
+ kpiTokens: "Tokens",
117573
+ kpiRequests: "Solicitudes",
117574
+ kpiAvgCost: "Costo promedio por solicitud",
117575
+ kpiTokensSub: "{value} por solicitud",
117576
+ kpiRequestsSub: "Llamadas a la IA",
117577
+ kpiAvgCostSub: "Gasto dividido entre solicitudes",
117578
+ chartTitle: "Gasto a lo largo del tiempo",
117579
+ chartSubtitleDay: "Gasto diario en dólares estadounidenses",
117580
+ chartSubtitleWeek: "Gasto semanal en dólares estadounidenses",
117581
+ chartSubtitleMonth: "Gasto mensual en dólares estadounidenses",
117582
+ chartAria: "Gasto a lo largo del tiempo. Usa las flechas izquierda y derecha para leer cada barra.",
117583
+ chartNoData: "No hay gasto en este período.",
117584
+ chartTableCaption: "Gasto por período",
117585
+ colPeriod: "Período",
117586
+ colCost: "Costo",
117587
+ byModelTitle: "Por modelo",
117588
+ byModelSubtitle: "Parte de tu gasto por modelo",
117589
+ byServiceTitle: "Por servicio",
117590
+ byServiceSubtitle: "De dónde proviene tu gasto",
117591
+ otherRow: "Otros",
117592
+ unknownModel: "Modelo desconocido",
117593
+ unknownService: "Otro servicio",
117594
+ breakdownEmpty: "No hay nada que desglosar en este período.",
117595
+ sectionError: "No se pudo cargar esta sección.",
117596
+ activityTitle: "Actividad reciente",
117597
+ activitySubtitle: "Tus solicitudes de IA más recientes, primero las más nuevas. Abre una para ver cada paso.",
117598
+ activityEmpty: "No hay solicitudes en este período.",
117599
+ colWhen: "Cuándo",
117600
+ colService: "Servicio",
117601
+ colSession: "Sesión",
117602
+ colLatency: "Latencia",
117603
+ colDetails: "Detalles",
117604
+ showSteps: "Mostrar pasos de {when}",
117605
+ hideSteps: "Ocultar pasos de {when}",
117606
+ stepsLoading: "Cargando pasos…",
117607
+ stepsEmpty: "No se registraron pasos para esta solicitud.",
117608
+ stepsError: "No se pudieron cargar los pasos de esta solicitud.",
117609
+ colStep: "Paso",
117610
+ colModel: "Modelo",
117611
+ colInput: "Entrada",
117612
+ colOutput: "Salida",
117613
+ colTotal: "Total",
117614
+ paginationSummary: "Mostrando {from}–{to} de {total}",
117615
+ previous: "Anterior",
117616
+ next: "Siguiente",
117617
+ emptyTitle: "Sin uso de IA en este período",
117618
+ emptyBody: "Los chats, la memoria y otras solicitudes de IA aparecen aquí cuando ocurren. Prueba un rango más amplio.",
117619
+ emptyAction90: "Mostrar los últimos 90 días",
117620
+ emptyActionAll: "Mostrar todo",
117621
+ errorForbiddenTitle: "El uso no está disponible",
117622
+ errorForbiddenBody: "No pudimos cargar el uso de esta cuenta.",
117623
+ errorUnavailableTitle: "El uso no está disponible temporalmente",
117624
+ errorUnavailableBody: "Inténtalo de nuevo en unos minutos.",
117625
+ errorUnauthorizedTitle: "Tu sesión ha expirado",
117626
+ errorUnauthorizedBody: "Vuelve a iniciar sesión para ver tu uso.",
117627
+ errorUnknownTitle: "Algo salió mal",
117628
+ errorUnknownBody: "No pudimos cargar tu uso.",
117629
+ tryAgain: "Intentar de nuevo",
117630
+ scopeUser: "Uso de {username}"
117631
+ };
117265
117632
  var es = {
117266
117633
  accessTabAddAccess: accessTabAddAccess$1,
117267
117634
  accessTabLabels: accessTabLabels$1,
@@ -117298,6 +117665,7 @@ var es = {
117298
117665
  analyticsAnalyticsReports: analyticsAnalyticsReports$1,
117299
117666
  analyticsAnalyticsTopicsStats: analyticsAnalyticsTopicsStats$1,
117300
117667
  analyticsAnalyticsTranscriptsStats: analyticsAnalyticsTranscriptsStats$1,
117668
+ analyticsTranscriptTurnDetails: analyticsTranscriptTurnDetails$1,
117301
117669
  analyticsAnalyticsUsersStats: analyticsAnalyticsUsersStats$1,
117302
117670
  analyticsGroupsFilterDropdown: analyticsGroupsFilterDropdown$1,
117303
117671
  analyticsStatCard: analyticsStatCard$1,
@@ -117545,7 +117913,8 @@ var es = {
117545
117913
  analyticsChartCard: analyticsChartCard$1,
117546
117914
  analyticsCostTabs: analyticsCostTabs$1,
117547
117915
  analyticsLlmUsage: analyticsLlmUsage$1,
117548
- analyticsTraces: analyticsTraces$1
117916
+ analyticsTraces: analyticsTraces$1,
117917
+ profileLlmUsage: profileLlmUsage$1
117549
117918
  };
117550
117919
 
117551
117920
  var accessTabAddAccess = {
@@ -118563,6 +118932,37 @@ var analyticsAnalyticsTranscriptsStats = {
118563
118932
  avgCostPerConversationDescription: "一次典型对话所产生的模型使用成本。",
118564
118933
  avgRatingDescription: "用户对其对话给出的平均评分,满分 5 分。"
118565
118934
  };
118935
+ var analyticsTranscriptTurnDetails = {
118936
+ showDetails: "显示详情",
118937
+ hideDetails: "隐藏详情",
118938
+ metadata: "元数据",
118939
+ requestContext: "请求上下文",
118940
+ model: "模型",
118941
+ provider: "提供商",
118942
+ temperature: "温度",
118943
+ client: "客户端",
118944
+ sourcesBadge: "文档 · {count}",
118945
+ toolsBadge: "工具 · {count}",
118946
+ yes: "是",
118947
+ no: "否",
118948
+ redacted: "已隐藏",
118949
+ fieldSession: "会话",
118950
+ fieldCallerVerified: "调用者已验证",
118951
+ fieldFiles: "文件",
118952
+ fieldFile: "文件",
118953
+ fieldSystemPrompt: "系统提示",
118954
+ fieldFlow: "流程",
118955
+ fieldToken: "令牌",
118956
+ fieldTenant: "租户",
118957
+ fieldPathway: "路径",
118958
+ fieldUsername: "用户名",
118959
+ fieldName: "名称",
118960
+ fieldCanvas: "画布",
118961
+ fieldProactivePrompt: "主动提示",
118962
+ fieldGrader: "评分器",
118963
+ fieldDatasets: "数据集",
118964
+ fieldDateTime: "日期和时间"
118965
+ };
118566
118966
  var analyticsAnalyticsUsersStats = {
118567
118967
  usersLoggedInNow: "当前在线用户",
118568
118968
  usersLoggedInPast30Days: "过去 30 天登录用户",
@@ -120219,7 +120619,8 @@ var historyTabLabels = {
120219
120619
  noResponse: "暂无回复",
120220
120620
  selectPrompt: "选择一条对话以查看详情。",
120221
120621
  aiAgent: "AI Agent",
120222
- noConversations: "未找到对话"
120622
+ noConversations: "未找到对话",
120623
+ documentsChip: "文档 · {count}"
120223
120624
  },
120224
120625
  summary: {
120225
120626
  notAvailable: "摘要不可用"
@@ -121344,7 +121745,13 @@ var profileAccount = {
121344
121745
  tabBenchmarks: "基准测试",
121345
121746
  descBenchmarks: "构建包含测试输入的基准测试套件,以评估智能体。",
121346
121747
  tabMemory: "记忆",
121347
- descMemory: "管理用户的全局记忆和智能体记忆。"
121748
+ descMemory: "管理用户的全局记忆和智能体记忆。",
121749
+ groupWorkspace: "工作区",
121750
+ groupAiData: "AI 与数据",
121751
+ groupSystem: "系统",
121752
+ ariaSettingsNav: "组织设置",
121753
+ ariaSettingsNavMobile: "组织设置(移动端)",
121754
+ groupFinance: "财务"
121348
121755
  };
121349
121756
  var profileAddCreditsModal = {
121350
121757
  title: "添加积分",
@@ -121540,7 +121947,6 @@ var profileIndex = {
121540
121947
  langSpanish: "西班牙语",
121541
121948
  langFrench: "法语",
121542
121949
  checkboxDisplayMentorAI: "显示助手侧边栏",
121543
- checkboxDisplayLeaderboard: "显示排行榜",
121544
121950
  labelFacebook: "Facebook",
121545
121951
  placeholderFacebook: "Facebook",
121546
121952
  labelLinkedIn: "LinkedIn",
@@ -121561,7 +121967,16 @@ var profileIndex = {
121561
121967
  descChatHistoryPreview: "与 AI 代理的对话。",
121562
121968
  ariaAddEducation: "添加教育经历",
121563
121969
  ariaAddExperience: "添加工作经历",
121564
- ariaAddResume: "添加简历"
121970
+ ariaAddResume: "添加简历",
121971
+ tabLlmUsage: "用量",
121972
+ descLlmUsage: "查看你使用了多少 AI:各对话中的花费、令牌数和请求数。",
121973
+ groupProfile: "个人资料",
121974
+ groupAiData: "AI 与数据",
121975
+ groupAccount: "账户",
121976
+ groupRecords: "记录",
121977
+ descLlmUsagePreview: "各对话中的花费、令牌数和请求数。",
121978
+ sectionFeatures: "功能",
121979
+ tooltipDisplayMentorAI: "让 AI 助手面板始终显示在当前页面旁边,无需离开页面即可提问。"
121565
121980
  };
121566
121981
  var profileInviteUserContent = {
121567
121982
  inviteUsersTitle: "邀请用户",
@@ -124420,6 +124835,81 @@ var analyticsTraces = {
124420
124835
  mentorScopeTitle: "代理范围仅缩小追踪列表。",
124421
124836
  mentorScopeBody: "行级观测记录无法按单个代理筛选 — 打开一条追踪查看它自己的调用。"
124422
124837
  };
124838
+ var profileLlmUsage = {
124839
+ rangeLabel: "日期范围",
124840
+ rangeToday: "今天",
124841
+ range7d: "7 天",
124842
+ range30d: "30 天",
124843
+ range90d: "90 天",
124844
+ rangeAllTime: "全部",
124845
+ periodToday: "今天",
124846
+ period7d: "最近 7 天",
124847
+ period30d: "最近 30 天",
124848
+ period90d: "最近 90 天",
124849
+ periodAllTime: "全部时间",
124850
+ scopeSelf: "仅显示你自己的活动",
124851
+ refresh: "刷新用量",
124852
+ kpiSpend: "花费",
124853
+ kpiTokens: "令牌",
124854
+ kpiRequests: "请求",
124855
+ kpiAvgCost: "每次请求平均花费",
124856
+ kpiTokensSub: "每次请求 {value}",
124857
+ kpiRequestsSub: "AI 调用",
124858
+ kpiAvgCostSub: "花费除以请求数",
124859
+ chartTitle: "花费趋势",
124860
+ chartSubtitleDay: "每日花费(美元)",
124861
+ chartSubtitleWeek: "每周花费(美元)",
124862
+ chartSubtitleMonth: "每月花费(美元)",
124863
+ chartAria: "花费趋势。使用左右方向键逐个读取柱形。",
124864
+ chartNoData: "此期间没有花费。",
124865
+ chartTableCaption: "各期间花费",
124866
+ colPeriod: "期间",
124867
+ colCost: "花费",
124868
+ byModelTitle: "按模型",
124869
+ byModelSubtitle: "各模型占你花费的比例",
124870
+ byServiceTitle: "按服务",
124871
+ byServiceSubtitle: "你的花费来自哪里",
124872
+ otherRow: "其他",
124873
+ unknownModel: "未知模型",
124874
+ unknownService: "其他服务",
124875
+ breakdownEmpty: "此期间没有可细分的数据。",
124876
+ sectionError: "无法加载此部分。",
124877
+ activityTitle: "最近活动",
124878
+ activitySubtitle: "你最近的 AI 请求,最新的在前。展开可查看每个步骤。",
124879
+ activityEmpty: "此期间没有请求。",
124880
+ colWhen: "时间",
124881
+ colService: "服务",
124882
+ colSession: "会话",
124883
+ colLatency: "延迟",
124884
+ colDetails: "详情",
124885
+ showSteps: "显示 {when} 的步骤",
124886
+ hideSteps: "隐藏 {when} 的步骤",
124887
+ stepsLoading: "正在加载步骤…",
124888
+ stepsEmpty: "此请求没有记录任何步骤。",
124889
+ stepsError: "无法加载此请求的步骤。",
124890
+ colStep: "步骤",
124891
+ colModel: "模型",
124892
+ colInput: "输入",
124893
+ colOutput: "输出",
124894
+ colTotal: "合计",
124895
+ paginationSummary: "第 {from}–{to} 条,共 {total} 条",
124896
+ previous: "上一页",
124897
+ next: "下一页",
124898
+ emptyTitle: "此期间没有 AI 用量",
124899
+ emptyBody: "发生聊天、记忆和其他 AI 请求后,相关记录会显示在这里。请尝试更长的时间范围。",
124900
+ emptyAction90: "显示最近 90 天",
124901
+ emptyActionAll: "显示全部",
124902
+ errorForbiddenTitle: "无法查看用量",
124903
+ errorForbiddenBody: "无法加载此账户的用量。",
124904
+ errorUnavailableTitle: "用量暂时不可用",
124905
+ errorUnavailableBody: "请几分钟后再试。",
124906
+ errorUnauthorizedTitle: "你的会话已过期",
124907
+ errorUnauthorizedBody: "请重新登录以查看你的用量。",
124908
+ errorUnknownTitle: "出现问题",
124909
+ errorUnknownBody: "无法加载你的用量。",
124910
+ tryAgain: "重试",
124911
+ scopeUser: "{username} 的用量"
124912
+ };
124423
124913
  var zh = {
124424
124914
  accessTabAddAccess: accessTabAddAccess,
124425
124915
  accessTabLabels: accessTabLabels,
@@ -124456,6 +124946,7 @@ var zh = {
124456
124946
  analyticsAnalyticsReports: analyticsAnalyticsReports,
124457
124947
  analyticsAnalyticsTopicsStats: analyticsAnalyticsTopicsStats,
124458
124948
  analyticsAnalyticsTranscriptsStats: analyticsAnalyticsTranscriptsStats,
124949
+ analyticsTranscriptTurnDetails: analyticsTranscriptTurnDetails,
124459
124950
  analyticsAnalyticsUsersStats: analyticsAnalyticsUsersStats,
124460
124951
  analyticsGroupsFilterDropdown: analyticsGroupsFilterDropdown,
124461
124952
  analyticsStatCard: analyticsStatCard,
@@ -124703,7 +125194,8 @@ var zh = {
124703
125194
  analyticsChartCard: analyticsChartCard,
124704
125195
  analyticsCostTabs: analyticsCostTabs,
124705
125196
  analyticsLlmUsage: analyticsLlmUsage,
124706
- analyticsTraces: analyticsTraces
125197
+ analyticsTraces: analyticsTraces,
125198
+ profileLlmUsage: profileLlmUsage
124707
125199
  };
124708
125200
 
124709
125201
  /**
@@ -131707,8 +132199,8 @@ function EducationDialog({ open, onOpenChange, org, username, education, onCompl
131707
132199
  }, org: org, username: username })] }));
131708
132200
  }
131709
132201
 
131710
- const Card = React.forwardRef(({ className, ...props }, ref) => (jsx("div", { ref: ref, className: cn("rounded-lg border bg-card text-card-foreground shadow-sm", className), ...props })));
131711
- Card.displayName = "Card";
132202
+ const Card$1 = React.forwardRef(({ className, ...props }, ref) => (jsx("div", { ref: ref, className: cn("rounded-lg border bg-card text-card-foreground shadow-sm", className), ...props })));
132203
+ Card$1.displayName = "Card";
131712
132204
  const CardHeader = React.forwardRef(({ className, ...props }, ref) => (jsx("div", { ref: ref, className: cn("flex flex-col space-y-1.5 p-6", className), ...props })));
131713
132205
  CardHeader.displayName = "CardHeader";
131714
132206
  const CardTitle = React.forwardRef(({ className, ...props }, ref) => (jsx("div", { ref: ref, className: cn("text-2xl font-semibold leading-none tracking-tight", className), ...props })));
@@ -131721,7 +132213,7 @@ const CardFooter = React.forwardRef(({ className, ...props }, ref) => (jsx("div"
131721
132213
  CardFooter.displayName = "CardFooter";
131722
132214
 
131723
132215
  function ProfileEmptyState({ icon: Icon, message }) {
131724
- return (jsx(Card, { className: "shadow-sm border border-dashed", style: { borderColor: 'oklch(.922 0 0)' }, children: jsxs(CardContent, { className: "p-8 text-center", children: [jsx(Icon, { className: "h-10 w-10 mx-auto text-gray-300 mb-3", "aria-hidden": "true" }), jsx("p", { className: "text-sm text-gray-400", children: message })] }) }));
132216
+ return (jsx(Card$1, { className: "shadow-sm border border-dashed", style: { borderColor: 'oklch(.922 0 0)' }, children: jsxs(CardContent, { className: "p-8 text-center", children: [jsx(Icon, { className: "h-10 w-10 mx-auto text-gray-300 mb-3", "aria-hidden": "true" }), jsx("p", { className: "text-sm text-gray-400", children: message })] }) }));
131725
132217
  }
131726
132218
 
131727
132219
  const formatEducationDuration = (education, present) => {
@@ -154144,15 +154636,15 @@ function SubscriptionDetail({ subscription: initialSubscription, platformKey, on
154144
154636
  if (isLoadingDetail) {
154145
154637
  return (jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-center gap-3", children: [jsxs(Button$1, { variant: "ghost", size: "sm", onClick: onBack, className: "cursor-pointer", children: [jsx(ChevronLeft, { className: "h-4 w-4 mr-1" }), t('purchasesPurchasesTab.back')] }), jsx(Skeleton, { className: "h-6 w-48" })] }), jsx(Skeleton, { className: "h-64 w-full" })] }));
154146
154638
  }
154147
- return (jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-center gap-3", children: [jsxs(Button$1, { variant: "ghost", size: "sm", onClick: onBack, className: "cursor-pointer", children: [jsx(ChevronLeft, { className: "h-4 w-4 mr-1" }), t('purchasesPurchasesTab.back')] }), jsxs("div", { children: [jsx("h4", { className: "text-base font-semibold text-gray-900 dark:text-gray-100", children: subscription.item_name || subscription.item_id }), jsx("span", { className: "text-xs text-gray-400 capitalize", children: subscription.item_type })] })] }), jsx(Card, { className: "shadow-sm border", style: { borderColor: 'oklch(.922 0 0)' }, children: jsxs(CardContent, { className: "p-6 space-y-5", children: [jsxs("div", { className: "flex items-center justify-between", children: [jsx("span", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: t('purchasesPurchasesTab.labelStatus') }), jsx(Badge, { className: statusBadgeStyle(subscription.status), children: subscription.status })] }), jsxs("div", { className: "flex items-center justify-between border-t pt-3", children: [jsx("span", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: t('purchasesPurchasesTab.labelPlan') }), jsxs("div", { className: "text-right", children: [jsx("span", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100", children: (_b = subscription.price) === null || _b === void 0 ? void 0 : _b.name }), jsxs("span", { className: "text-sm text-gray-500 ml-2", children: [formatCurrency((_c = subscription.price) === null || _c === void 0 ? void 0 : _c.amount, (_d = subscription.price) === null || _d === void 0 ? void 0 : _d.currency), formatInterval((_e = subscription.price) === null || _e === void 0 ? void 0 : _e.interval)] })] })] }), jsxs("div", { className: "flex items-center justify-between border-t pt-3", children: [jsx("span", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: t('purchasesPurchasesTab.labelType') }), jsx(Badge, { variant: "secondary", className: "capitalize", children: subscription.item_type })] }), subscription.current_period_end && (jsxs("div", { className: "flex items-center justify-between border-t pt-3", children: [jsx("span", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: subscription.cancel_at_period_end
154639
+ return (jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-center gap-3", children: [jsxs(Button$1, { variant: "ghost", size: "sm", onClick: onBack, className: "cursor-pointer", children: [jsx(ChevronLeft, { className: "h-4 w-4 mr-1" }), t('purchasesPurchasesTab.back')] }), jsxs("div", { children: [jsx("h4", { className: "text-base font-semibold text-gray-900 dark:text-gray-100", children: subscription.item_name || subscription.item_id }), jsx("span", { className: "text-xs text-gray-400 capitalize", children: subscription.item_type })] })] }), jsx(Card$1, { className: "shadow-sm border", style: { borderColor: 'oklch(.922 0 0)' }, children: jsxs(CardContent, { className: "p-6 space-y-5", children: [jsxs("div", { className: "flex items-center justify-between", children: [jsx("span", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: t('purchasesPurchasesTab.labelStatus') }), jsx(Badge, { className: statusBadgeStyle(subscription.status), children: subscription.status })] }), jsxs("div", { className: "flex items-center justify-between border-t pt-3", children: [jsx("span", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: t('purchasesPurchasesTab.labelPlan') }), jsxs("div", { className: "text-right", children: [jsx("span", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100", children: (_b = subscription.price) === null || _b === void 0 ? void 0 : _b.name }), jsxs("span", { className: "text-sm text-gray-500 ml-2", children: [formatCurrency((_c = subscription.price) === null || _c === void 0 ? void 0 : _c.amount, (_d = subscription.price) === null || _d === void 0 ? void 0 : _d.currency), formatInterval((_e = subscription.price) === null || _e === void 0 ? void 0 : _e.interval)] })] })] }), jsxs("div", { className: "flex items-center justify-between border-t pt-3", children: [jsx("span", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: t('purchasesPurchasesTab.labelType') }), jsx(Badge, { variant: "secondary", className: "capitalize", children: subscription.item_type })] }), subscription.current_period_end && (jsxs("div", { className: "flex items-center justify-between border-t pt-3", children: [jsx("span", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: subscription.cancel_at_period_end
154148
154640
  ? t('purchasesPurchasesTab.labelAccessUntil')
154149
- : t('purchasesPurchasesTab.labelRenewsOn') }), jsxs("span", { className: "text-sm text-gray-600 dark:text-gray-400 flex items-center gap-1.5", children: [jsx(Calendar$1, { className: "h-3.5 w-3.5" }), new Date(subscription.current_period_end).toLocaleDateString()] })] })), subscription.created_at && (jsxs("div", { className: "flex items-center justify-between border-t pt-3", children: [jsx("span", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: t('purchasesPurchasesTab.labelSubscribedSince') }), jsxs("span", { className: "text-sm text-gray-600 dark:text-gray-400 flex items-center gap-1.5", children: [jsx(Clock, { className: "h-3.5 w-3.5" }), new Date(subscription.created_at).toLocaleDateString()] })] })), subscription.is_grandfathered && (jsxs("div", { className: "flex items-center justify-between border-t pt-3", children: [jsx("span", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: t('purchasesPurchasesTab.labelLegacyAccess') }), jsxs(Badge, { className: "bg-amber-100 text-amber-700 border-amber-200 text-xs", children: [jsx(Shield, { className: "h-3 w-3 mr-1" }), t('purchasesPurchasesTab.badgeGrandfathered')] })] })), subscription.cancel_at_period_end && (jsxs("div", { className: "flex items-center gap-2 border-t pt-3 text-yellow-600", children: [jsx(TriangleAlert, { className: "h-4 w-4 flex-shrink-0" }), jsx("span", { className: "text-sm", children: t('purchasesPurchasesTab.warningWillNotRenew') })] }))] }) }), canCancel && !cancelResult && (jsx(Card, { className: "shadow-sm border border-blue-100", style: { borderColor: 'oklch(.922 0 0)' }, children: jsxs(CardContent, { className: "p-5 space-y-3", children: [jsx("h4", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: t('purchasesPurchasesTab.cancelSectionHeading') }), jsx("p", { className: "text-xs text-gray-500", children: t('purchasesPurchasesTab.cancelSectionDescription') }), jsxs("div", { className: "flex gap-2 items-end", children: [jsxs("div", { className: "flex-1", children: [jsxs("label", { className: "text-xs text-gray-500 mb-1 block", children: [t('purchasesPurchasesTab.cancelConfirmLabelPrefix'), ' ', jsx("span", { className: "font-mono font-bold", children: "cancel" }), ' ', t('purchasesPurchasesTab.cancelConfirmLabelSuffix')] }), jsx(Input, { value: confirmText, onChange: (e) => setConfirmText(e.target.value), placeholder: t('purchasesPurchasesTab.cancelConfirmPlaceholder') })] }), jsxs(Button$1, { onClick: handleCancel, disabled: confirmText !== 'cancel' || isCanceling, className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] hover:opacity-90 text-white cursor-pointer", children: [isCanceling ? (jsx(LoaderCircle, { className: "h-4 w-4 mr-2 animate-spin" })) : (jsx(CircleX, { className: "h-4 w-4 mr-2" })), t('purchasesPurchasesTab.cancelButton')] })] })] }) })), (cancelResult === null || cancelResult === void 0 ? void 0 : cancelResult.canceled) && (jsx(Card, { className: "shadow-sm border border-green-200", style: { borderColor: 'oklch(.922 0 0)' }, children: jsxs(CardContent, { className: "p-5 text-center", children: [jsx(CircleX, { className: "h-8 w-8 text-gray-400 mx-auto mb-2" }), jsx("h4", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100 mb-1", children: t('purchasesPurchasesTab.canceledHeading') }), jsx("p", { className: "text-xs text-gray-500", children: t('purchasesPurchasesTab.canceledDescription') }), jsx(Button$1, { variant: "outline", size: "icon", onClick: onBack, "aria-label": t('purchasesPurchasesTab.backToPurchases'), className: "mt-3 h-8 w-8 cursor-pointer rounded-full border-0 bg-gray-100 text-gray-700 hover:bg-gray-200 hover:text-gray-900 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-gray-700 dark:hover:text-white", children: jsx(ArrowLeft, { className: "h-4 w-4" }) })] }) })), (cancelResult === null || cancelResult === void 0 ? void 0 : cancelResult.portalUrl) && (jsx(Card, { className: "shadow-sm border border-blue-200", style: { borderColor: 'oklch(.922 0 0)' }, children: jsxs(CardContent, { className: "p-5 text-center", children: [jsx(ExternalLink, { className: "h-8 w-8 text-blue-400 mx-auto mb-2" }), jsx("h4", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100 mb-1", children: t('purchasesPurchasesTab.stripePortalHeading') }), jsx("p", { className: "text-xs text-gray-500 mb-3", children: t('purchasesPurchasesTab.stripePortalDescription') }), jsxs(Button$1, { onClick: () => window.open(cancelResult.portalUrl, '_blank'), className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] hover:opacity-90 text-white cursor-pointer", children: [jsx(ExternalLink, { className: "h-4 w-4 mr-2" }), t('purchasesPurchasesTab.openStripePortal')] })] }) }))] }));
154641
+ : t('purchasesPurchasesTab.labelRenewsOn') }), jsxs("span", { className: "text-sm text-gray-600 dark:text-gray-400 flex items-center gap-1.5", children: [jsx(Calendar$1, { className: "h-3.5 w-3.5" }), new Date(subscription.current_period_end).toLocaleDateString()] })] })), subscription.created_at && (jsxs("div", { className: "flex items-center justify-between border-t pt-3", children: [jsx("span", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: t('purchasesPurchasesTab.labelSubscribedSince') }), jsxs("span", { className: "text-sm text-gray-600 dark:text-gray-400 flex items-center gap-1.5", children: [jsx(Clock, { className: "h-3.5 w-3.5" }), new Date(subscription.created_at).toLocaleDateString()] })] })), subscription.is_grandfathered && (jsxs("div", { className: "flex items-center justify-between border-t pt-3", children: [jsx("span", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: t('purchasesPurchasesTab.labelLegacyAccess') }), jsxs(Badge, { className: "bg-amber-100 text-amber-700 border-amber-200 text-xs", children: [jsx(Shield, { className: "h-3 w-3 mr-1" }), t('purchasesPurchasesTab.badgeGrandfathered')] })] })), subscription.cancel_at_period_end && (jsxs("div", { className: "flex items-center gap-2 border-t pt-3 text-yellow-600", children: [jsx(TriangleAlert, { className: "h-4 w-4 flex-shrink-0" }), jsx("span", { className: "text-sm", children: t('purchasesPurchasesTab.warningWillNotRenew') })] }))] }) }), canCancel && !cancelResult && (jsx(Card$1, { className: "shadow-sm border border-blue-100", style: { borderColor: 'oklch(.922 0 0)' }, children: jsxs(CardContent, { className: "p-5 space-y-3", children: [jsx("h4", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: t('purchasesPurchasesTab.cancelSectionHeading') }), jsx("p", { className: "text-xs text-gray-500", children: t('purchasesPurchasesTab.cancelSectionDescription') }), jsxs("div", { className: "flex gap-2 items-end", children: [jsxs("div", { className: "flex-1", children: [jsxs("label", { className: "text-xs text-gray-500 mb-1 block", children: [t('purchasesPurchasesTab.cancelConfirmLabelPrefix'), ' ', jsx("span", { className: "font-mono font-bold", children: "cancel" }), ' ', t('purchasesPurchasesTab.cancelConfirmLabelSuffix')] }), jsx(Input, { value: confirmText, onChange: (e) => setConfirmText(e.target.value), placeholder: t('purchasesPurchasesTab.cancelConfirmPlaceholder') })] }), jsxs(Button$1, { onClick: handleCancel, disabled: confirmText !== 'cancel' || isCanceling, className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] hover:opacity-90 text-white cursor-pointer", children: [isCanceling ? (jsx(LoaderCircle, { className: "h-4 w-4 mr-2 animate-spin" })) : (jsx(CircleX, { className: "h-4 w-4 mr-2" })), t('purchasesPurchasesTab.cancelButton')] })] })] }) })), (cancelResult === null || cancelResult === void 0 ? void 0 : cancelResult.canceled) && (jsx(Card$1, { className: "shadow-sm border border-green-200", style: { borderColor: 'oklch(.922 0 0)' }, children: jsxs(CardContent, { className: "p-5 text-center", children: [jsx(CircleX, { className: "h-8 w-8 text-gray-400 mx-auto mb-2" }), jsx("h4", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100 mb-1", children: t('purchasesPurchasesTab.canceledHeading') }), jsx("p", { className: "text-xs text-gray-500", children: t('purchasesPurchasesTab.canceledDescription') }), jsx(Button$1, { variant: "outline", size: "icon", onClick: onBack, "aria-label": t('purchasesPurchasesTab.backToPurchases'), className: "mt-3 h-8 w-8 cursor-pointer rounded-full border-0 bg-gray-100 text-gray-700 hover:bg-gray-200 hover:text-gray-900 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-gray-700 dark:hover:text-white", children: jsx(ArrowLeft, { className: "h-4 w-4" }) })] }) })), (cancelResult === null || cancelResult === void 0 ? void 0 : cancelResult.portalUrl) && (jsx(Card$1, { className: "shadow-sm border border-blue-200", style: { borderColor: 'oklch(.922 0 0)' }, children: jsxs(CardContent, { className: "p-5 text-center", children: [jsx(ExternalLink, { className: "h-8 w-8 text-blue-400 mx-auto mb-2" }), jsx("h4", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100 mb-1", children: t('purchasesPurchasesTab.stripePortalHeading') }), jsx("p", { className: "text-xs text-gray-500 mb-3", children: t('purchasesPurchasesTab.stripePortalDescription') }), jsxs(Button$1, { onClick: () => window.open(cancelResult.portalUrl, '_blank'), className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] hover:opacity-90 text-white cursor-pointer", children: [jsx(ExternalLink, { className: "h-4 w-4 mr-2" }), t('purchasesPurchasesTab.openStripePortal')] })] }) }))] }));
154150
154642
  }
154151
154643
  // ── Subscription Card (List Item) ──
154152
154644
  function SubscriptionCard({ sub, onClick }) {
154153
154645
  var _a, _b, _c, _d, _e, _f, _g;
154154
154646
  const t = useT();
154155
- return (jsx(Card, { className: "shadow-sm border hover:border-blue-200 transition-colors cursor-pointer", style: { borderColor: 'oklch(.922 0 0)' }, onClick: onClick, children: jsx(CardContent, { className: "p-4", children: jsxs("div", { className: "flex items-start justify-between", children: [jsxs("div", { className: "flex-1 min-w-0", children: [jsxs("div", { className: "flex items-center gap-2 mb-1", children: [jsx("h4", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100 truncate", children: sub.item_name || sub.item_id }), jsx(Badge, { variant: "secondary", className: "text-xs capitalize flex-shrink-0", children: sub.item_type })] }), jsxs("div", { className: "flex items-center gap-2 text-sm text-gray-600", children: [jsx(CreditCard, { className: "h-3.5 w-3.5 text-gray-400" }), jsxs("span", { children: [(_a = sub.price) === null || _a === void 0 ? void 0 : _a.name, " \u2014", ' ', jsxs("span", { className: "font-semibold", children: [formatCurrency((_b = sub.price) === null || _b === void 0 ? void 0 : _b.amount, (_c = sub.price) === null || _c === void 0 ? void 0 : _c.currency), ((_d = sub.price) === null || _d === void 0 ? void 0 : _d.interval) === 'month'
154647
+ return (jsx(Card$1, { className: "shadow-sm border hover:border-blue-200 transition-colors cursor-pointer", style: { borderColor: 'oklch(.922 0 0)' }, onClick: onClick, children: jsx(CardContent, { className: "p-4", children: jsxs("div", { className: "flex items-start justify-between", children: [jsxs("div", { className: "flex-1 min-w-0", children: [jsxs("div", { className: "flex items-center gap-2 mb-1", children: [jsx("h4", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100 truncate", children: sub.item_name || sub.item_id }), jsx(Badge, { variant: "secondary", className: "text-xs capitalize flex-shrink-0", children: sub.item_type })] }), jsxs("div", { className: "flex items-center gap-2 text-sm text-gray-600", children: [jsx(CreditCard, { className: "h-3.5 w-3.5 text-gray-400" }), jsxs("span", { children: [(_a = sub.price) === null || _a === void 0 ? void 0 : _a.name, " \u2014", ' ', jsxs("span", { className: "font-semibold", children: [formatCurrency((_b = sub.price) === null || _b === void 0 ? void 0 : _b.amount, (_c = sub.price) === null || _c === void 0 ? void 0 : _c.currency), ((_d = sub.price) === null || _d === void 0 ? void 0 : _d.interval) === 'month'
154156
154648
  ? t('purchasesPurchasesTab.intervalMonth')
154157
154649
  : ((_e = sub.price) === null || _e === void 0 ? void 0 : _e.interval) === 'year'
154158
154650
  ? t('purchasesPurchasesTab.intervalYear')
@@ -154189,7 +154681,7 @@ function PurchasesTab({ org, username }) {
154189
154681
  refetch();
154190
154682
  } }));
154191
154683
  }
154192
- return (jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "relative", children: [jsx(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400" }), jsx(Input, { value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), placeholder: t('purchasesPurchasesTab.searchPlaceholder'), className: "pl-9" }), isFetching && !isLoading && (jsx(LoaderCircle, { className: "absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 animate-spin text-gray-400" }))] }), jsxs("div", { className: "flex flex-col sm:flex-row gap-2", children: [jsxs(Select$1, { value: statusFilter, onValueChange: setStatusFilter, children: [jsx(SelectTrigger, { className: "w-full sm:w-[160px]", children: jsx(SelectValue, { placeholder: t('purchasesPurchasesTab.filterStatusPlaceholder') }) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "all", children: t('purchasesPurchasesTab.filterAllStatuses') }), jsx(SelectItem, { value: "active", children: t('purchasesPurchasesTab.filterActive') }), jsx(SelectItem, { value: "trialing", children: t('purchasesPurchasesTab.filterTrialing') }), jsx(SelectItem, { value: "canceled", children: t('purchasesPurchasesTab.filterCanceled') }), jsx(SelectItem, { value: "past_due", children: t('purchasesPurchasesTab.filterPastDue') })] })] }), jsxs(Select$1, { value: typeFilter, onValueChange: setTypeFilter, children: [jsx(SelectTrigger, { className: "w-full sm:w-[160px]", children: jsx(SelectValue, { placeholder: t('purchasesPurchasesTab.filterTypePlaceholder') }) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "all", children: t('purchasesPurchasesTab.filterAllTypes') }), jsx(SelectItem, { value: "mentor", children: t('purchasesPurchasesTab.filterAgent') }), jsx(SelectItem, { value: "course", children: t('purchasesPurchasesTab.filterCourse') }), jsx(SelectItem, { value: "program", children: t('purchasesPurchasesTab.filterProgram') }), jsx(SelectItem, { value: "pathway", children: t('purchasesPurchasesTab.filterPathway') })] })] }), data && (jsx("span", { className: "text-xs text-gray-400 self-center ml-auto", children: t('purchasesPurchasesTab.purchaseCount', { count: data.count }) }))] }), isLoading ? (jsx("div", { className: "space-y-2", children: [1, 2, 3].map((i) => (jsx(Skeleton, { className: "h-24 w-full" }, i))) })) : (data === null || data === void 0 ? void 0 : data.results) && data.results.length > 0 ? (jsxs(Fragment$1, { children: [jsx("div", { className: "space-y-2", children: data.results.map((sub) => (jsx(SubscriptionCard, { sub: sub, onClick: () => setSelectedSubscription(sub) }, sub.unique_id))) }), (data.previous_page !== null || data.next_page !== null) && (jsxs("div", { className: "flex items-center justify-between pt-2", children: [jsx("span", { className: "text-xs text-gray-400", children: t('purchasesPurchasesTab.paginationTotal', { count: data.count }) }), jsxs("div", { className: "flex items-center gap-2", children: [jsx(Button$1, { variant: "outline", size: "sm", disabled: data.previous_page === null, onClick: () => data.previous_page !== null && setPage(data.previous_page), className: "h-7 text-xs cursor-pointer", children: t('purchasesPurchasesTab.previousPage') }), jsx("span", { className: "text-xs text-gray-500", children: t('purchasesPurchasesTab.pageIndicator', { page }) }), jsx(Button$1, { variant: "outline", size: "sm", disabled: data.next_page === null, onClick: () => data.next_page !== null && setPage(data.next_page), className: "h-7 text-xs cursor-pointer", children: t('purchasesPurchasesTab.nextPage') })] })] }))] })) : (jsx(Card, { className: "shadow-sm border border-dashed", style: { borderColor: 'oklch(.922 0 0)' }, children: jsxs(CardContent, { className: "p-8 text-center", children: [jsx(ShoppingBag, { className: "h-10 w-10 mx-auto text-gray-300 mb-3" }), jsx("p", { className: "text-sm text-gray-400", children: debouncedSearch.trim()
154684
+ return (jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "relative", children: [jsx(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400" }), jsx(Input, { value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), placeholder: t('purchasesPurchasesTab.searchPlaceholder'), className: "pl-9" }), isFetching && !isLoading && (jsx(LoaderCircle, { className: "absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 animate-spin text-gray-400" }))] }), jsxs("div", { className: "flex flex-col sm:flex-row gap-2", children: [jsxs(Select$1, { value: statusFilter, onValueChange: setStatusFilter, children: [jsx(SelectTrigger, { className: "w-full sm:w-[160px]", children: jsx(SelectValue, { placeholder: t('purchasesPurchasesTab.filterStatusPlaceholder') }) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "all", children: t('purchasesPurchasesTab.filterAllStatuses') }), jsx(SelectItem, { value: "active", children: t('purchasesPurchasesTab.filterActive') }), jsx(SelectItem, { value: "trialing", children: t('purchasesPurchasesTab.filterTrialing') }), jsx(SelectItem, { value: "canceled", children: t('purchasesPurchasesTab.filterCanceled') }), jsx(SelectItem, { value: "past_due", children: t('purchasesPurchasesTab.filterPastDue') })] })] }), jsxs(Select$1, { value: typeFilter, onValueChange: setTypeFilter, children: [jsx(SelectTrigger, { className: "w-full sm:w-[160px]", children: jsx(SelectValue, { placeholder: t('purchasesPurchasesTab.filterTypePlaceholder') }) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "all", children: t('purchasesPurchasesTab.filterAllTypes') }), jsx(SelectItem, { value: "mentor", children: t('purchasesPurchasesTab.filterAgent') }), jsx(SelectItem, { value: "course", children: t('purchasesPurchasesTab.filterCourse') }), jsx(SelectItem, { value: "program", children: t('purchasesPurchasesTab.filterProgram') }), jsx(SelectItem, { value: "pathway", children: t('purchasesPurchasesTab.filterPathway') })] })] }), data && (jsx("span", { className: "text-xs text-gray-400 self-center ml-auto", children: t('purchasesPurchasesTab.purchaseCount', { count: data.count }) }))] }), isLoading ? (jsx("div", { className: "space-y-2", children: [1, 2, 3].map((i) => (jsx(Skeleton, { className: "h-24 w-full" }, i))) })) : (data === null || data === void 0 ? void 0 : data.results) && data.results.length > 0 ? (jsxs(Fragment$1, { children: [jsx("div", { className: "space-y-2", children: data.results.map((sub) => (jsx(SubscriptionCard, { sub: sub, onClick: () => setSelectedSubscription(sub) }, sub.unique_id))) }), (data.previous_page !== null || data.next_page !== null) && (jsxs("div", { className: "flex items-center justify-between pt-2", children: [jsx("span", { className: "text-xs text-gray-400", children: t('purchasesPurchasesTab.paginationTotal', { count: data.count }) }), jsxs("div", { className: "flex items-center gap-2", children: [jsx(Button$1, { variant: "outline", size: "sm", disabled: data.previous_page === null, onClick: () => data.previous_page !== null && setPage(data.previous_page), className: "h-7 text-xs cursor-pointer", children: t('purchasesPurchasesTab.previousPage') }), jsx("span", { className: "text-xs text-gray-500", children: t('purchasesPurchasesTab.pageIndicator', { page }) }), jsx(Button$1, { variant: "outline", size: "sm", disabled: data.next_page === null, onClick: () => data.next_page !== null && setPage(data.next_page), className: "h-7 text-xs cursor-pointer", children: t('purchasesPurchasesTab.nextPage') })] })] }))] })) : (jsx(Card$1, { className: "shadow-sm border border-dashed", style: { borderColor: 'oklch(.922 0 0)' }, children: jsxs(CardContent, { className: "p-8 text-center", children: [jsx(ShoppingBag, { className: "h-10 w-10 mx-auto text-gray-300 mb-3" }), jsx("p", { className: "text-sm text-gray-400", children: debouncedSearch.trim()
154193
154685
  ? t('purchasesPurchasesTab.emptySearchResult')
154194
154686
  : t('purchasesPurchasesTab.emptyNoPurchases') })] }) }))] }));
154195
154687
  }
@@ -165098,7 +165590,7 @@ function formatLocale(locale) {
165098
165590
  "q": formatQuarter,
165099
165591
  "Q": formatUnixTimestamp,
165100
165592
  "s": formatUnixTimestampSeconds,
165101
- "S": formatSeconds,
165593
+ "S": formatSeconds$1,
165102
165594
  "u": formatWeekdayNumberMonday,
165103
165595
  "U": formatWeekNumberSunday,
165104
165596
  "V": formatWeekNumberISO,
@@ -165564,7 +166056,7 @@ function formatMinutes(d, p) {
165564
166056
  return pad$1(d.getMinutes(), p, 2);
165565
166057
  }
165566
166058
 
165567
- function formatSeconds(d, p) {
166059
+ function formatSeconds$1(d, p) {
165568
166060
  return pad$1(d.getSeconds(), p, 2);
165569
166061
  }
165570
166062
 
@@ -173316,7 +173808,7 @@ var objectCtorString = funcToString.call(Object);
173316
173808
  * _.isPlainObject(Object.create(null));
173317
173809
  * // => true
173318
173810
  */
173319
- function isPlainObject$2(value) {
173811
+ function isPlainObject$3(value) {
173320
173812
  if (!isObjectLike$1(value) || baseGetTag$1(value) != objectTag) {
173321
173813
  return false;
173322
173814
  }
@@ -173329,9 +173821,9 @@ function isPlainObject$2(value) {
173329
173821
  funcToString.call(Ctor) == objectCtorString;
173330
173822
  }
173331
173823
 
173332
- var isPlainObject_1 = isPlainObject$2;
173824
+ var isPlainObject_1 = isPlainObject$3;
173333
173825
 
173334
- var isPlainObject$1 = /*@__PURE__*/getDefaultExportFromCjs(isPlainObject_1);
173826
+ var isPlainObject$2 = /*@__PURE__*/getDefaultExportFromCjs(isPlainObject_1);
173335
173827
 
173336
173828
  var baseGetTag = _baseGetTag,
173337
173829
  isObjectLike = isObjectLike_1;
@@ -173548,7 +174040,7 @@ function Shape(_ref2) {
173548
174040
  shape = /*#__PURE__*/cloneElement(option, _objectSpread$e(_objectSpread$e({}, props), getPropsFromShapeOption(option)));
173549
174041
  } else if (isFunction$3(option)) {
173550
174042
  shape = option(props);
173551
- } else if (isPlainObject$1(option) && !isBoolean(option)) {
174043
+ } else if (isPlainObject$2(option) && !isBoolean(option)) {
173552
174044
  var nextProps = propTransformer(option, props);
173553
174045
  shape = /*#__PURE__*/React__default.createElement(ShapeSelector, {
173554
174046
  shapeType: shapeType,
@@ -178833,7 +179325,7 @@ function useGradebookData(type, org, username, page, search) {
178833
179325
  page,
178834
179326
  page_size: PAGE_SIZE$e,
178835
179327
  platform_key: org,
178836
- ...(trimmed ? { search: trimmed } : {}),
179328
+ ...(trimmed ? { course_name: trimmed } : {}),
178837
179329
  },
178838
179330
  }, { skip: type !== 'courses', refetchOnMountOrArgChange: true });
178839
179331
  const programsQ = useGetUserEnrolledProgramsQuery([{ username, platformKey: org }], { skip: type !== 'programs' });
@@ -178984,7 +179476,7 @@ function GradebookCard({ row, icon: Icon, onClick, }) {
178984
179476
  var _a;
178985
179477
  const t = useT();
178986
179478
  const clickable = !!onClick;
178987
- return (jsx(Card, { className: cn('shadow-sm border transition-colors', clickable && 'hover:border-blue-200 cursor-pointer'), style: { borderColor: 'oklch(.922 0 0)' }, onClick: onClick, role: clickable ? 'button' : undefined, tabIndex: clickable ? 0 : undefined, onKeyDown: clickable
179479
+ return (jsx(Card$1, { className: cn('shadow-sm border transition-colors', clickable && 'hover:border-blue-200 cursor-pointer'), style: { borderColor: 'oklch(.922 0 0)' }, onClick: onClick, role: clickable ? 'button' : undefined, tabIndex: clickable ? 0 : undefined, onKeyDown: clickable
178988
179480
  ? (e) => {
178989
179481
  if (e.key === 'Enter' || e.key === ' ') {
178990
179482
  e.preventDefault();
@@ -218053,6 +218545,100 @@ function Markdown({ children, className }) {
218053
218545
  return (jsx("div", { className: className, children: jsx(Markdown$1, { components: components, children: children }) }));
218054
218546
  }
218055
218547
 
218548
+ function FileCard({ fileName, fileType }) {
218549
+ const getFileIcon = (fileName, fileType) => {
218550
+ const type = fileType.toLowerCase();
218551
+ const name = fileName.toLowerCase();
218552
+ const extension = name.split('.').pop() || '';
218553
+ // Check by MIME type first
218554
+ if (type.includes('pdf')) {
218555
+ return jsx(File$1, { className: "h-5 w-5 text-blue-500" });
218556
+ }
218557
+ else if (type.includes('image')) {
218558
+ return jsx(FileImage, { className: "h-5 w-5 text-blue-500" });
218559
+ }
218560
+ else if (type.includes('audio')) {
218561
+ return jsx(FileAudio, { className: "h-5 w-5 text-blue-500" });
218562
+ }
218563
+ else if (type.includes('video')) {
218564
+ return jsx(FilePlay, { className: "h-5 w-5 text-blue-500" });
218565
+ }
218566
+ else if (type.includes('spreadsheet') || type.includes('excel') || type.includes('csv')) {
218567
+ return jsx(FileSpreadsheet, { className: "h-5 w-5 text-blue-500" });
218568
+ }
218569
+ else if (type.includes('zip') || type.includes('compressed') || type.includes('archive')) {
218570
+ return jsx(FileArchive, { className: "h-5 w-5 text-blue-500" });
218571
+ }
218572
+ else if (type.includes('javascript') ||
218573
+ type.includes('typescript') ||
218574
+ type.includes('html') ||
218575
+ type.includes('css') ||
218576
+ type.includes('json')) {
218577
+ return jsx(FileCode, { className: "h-5 w-5 text-blue-500" });
218578
+ }
218579
+ // If MIME type doesn't match, check by extension
218580
+ if (['doc', 'docx', 'txt', 'rtf'].includes(extension)) {
218581
+ return jsx(FileText, { className: "h-5 w-5 text-blue-500" });
218582
+ }
218583
+ else if (['jpg', 'jpeg', 'png', 'gif', 'svg', 'webp'].includes(extension)) {
218584
+ return jsx(FileImage, { className: "h-5 w-5 text-blue-500" });
218585
+ }
218586
+ else if (['pdf'].includes(extension)) {
218587
+ return jsx(File$1, { className: "h-5 w-5 text-blue-500" });
218588
+ }
218589
+ else if (['mp3', 'wav', 'ogg', 'flac'].includes(extension)) {
218590
+ return jsx(FileAudio, { className: "h-5 w-5 text-blue-500" });
218591
+ }
218592
+ else if (['mp4', 'webm', 'avi', 'mov'].includes(extension)) {
218593
+ return jsx(FilePlay, { className: "h-5 w-5 text-blue-500" });
218594
+ }
218595
+ else if (['xls', 'xlsx', 'csv'].includes(extension)) {
218596
+ return jsx(FileSpreadsheet, { className: "h-5 w-5 text-blue-500" });
218597
+ }
218598
+ else if (['zip', 'rar', '7z', 'tar', 'gz'].includes(extension)) {
218599
+ return jsx(FileArchive, { className: "h-5 w-5 text-blue-500" });
218600
+ }
218601
+ else if (['js', 'ts', 'jsx', 'tsx', 'html', 'css', 'json', 'py', 'java', 'c', 'cpp'].includes(extension)) {
218602
+ return jsx(FileCode, { className: "h-5 w-5 text-blue-500" });
218603
+ }
218604
+ // Default icon
218605
+ return jsx(File$1, { className: "h-5 w-5 text-blue-500" });
218606
+ };
218607
+ return (jsxs("div", { className: "mb-2 flex max-w-xs items-center rounded-lg border border-gray-200 bg-white p-3", children: [jsx("div", { className: "mr-3 shrink-0 rounded-lg bg-blue-100 p-2 text-blue-600", children: getFileIcon(fileName, fileType) }), jsxs("div", { className: "min-w-0 flex-1", children: [jsx("h3", { className: "truncate text-sm font-medium text-gray-900", children: fileName }), jsx("p", { className: "truncate text-xs text-gray-500", children: fileType })] })] }));
218608
+ }
218609
+
218610
+ function ImageMessage({ url, fileName, setPreviewImage }) {
218611
+ const [hasError, setHasError] = useState(false);
218612
+ if (hasError || !url) {
218613
+ return jsx(FileCard, { fileName: fileName, fileType: "image/*" });
218614
+ }
218615
+ return (jsx("div", { className: "h-full max-h-64 w-full max-w-96 overflow-hidden rounded-lg", children: jsx("div", { className: "relative", children: jsx("img", { src: url, alt: fileName, className: "h-auto w-full cursor-pointer object-contain p-2", onClick: () => setPreviewImage(url), onError: () => setHasError(true) }) }) }));
218616
+ }
218617
+
218618
+ const openInNewTab = (url) => {
218619
+ window.open(url, '_blank', 'noopener,noreferrer');
218620
+ };
218621
+ /**
218622
+ * Files on an admin transcript, in the chat's own attachment idiom: images
218623
+ * preview like a sent image, everything else is the chat's file card. A card
218624
+ * with a URL opens the file in a new tab (presigned links expire, so nothing
218625
+ * is cached here).
218626
+ */
218627
+ function TranscriptFileCards({ files, className, }) {
218628
+ const shown = files.filter((file) => file === null || file === void 0 ? void 0 : file.name);
218629
+ if (shown.length === 0)
218630
+ return null;
218631
+ return (jsx("div", { className: className !== null && className !== void 0 ? className : 'mt-2 flex flex-wrap items-start gap-2', "data-testid": "transcript-file-cards", children: shown.map((file, index) => {
218632
+ var _a;
218633
+ const contentType = (_a = file.contentType) !== null && _a !== void 0 ? _a : '';
218634
+ if (file.url && contentType.startsWith('image/')) {
218635
+ return (jsx(ImageMessage, { url: file.url, fileName: file.name, setPreviewImage: openInNewTab }, `${file.name}-${index}`));
218636
+ }
218637
+ const card = jsx(FileCard, { fileName: file.name, fileType: contentType || 'file' });
218638
+ return file.url ? (jsx("a", { href: file.url, target: "_blank", rel: "noopener noreferrer", className: "block hover:opacity-90", "aria-label": `Open ${file.name}`, onClick: (event) => event.stopPropagation(), children: card }, `${file.name}-${index}`)) : (jsx("div", { children: card }, `${file.name}-${index}`));
218639
+ }) }));
218640
+ }
218641
+
218056
218642
  /**
218057
218643
  * Whether the dropdown is open, closing on a pointer press outside `ref` or on
218058
218644
  * Escape.
@@ -218696,8 +219282,12 @@ function ConversationMessages({ messages, username, agent, fallbackEmail, }) {
218696
219282
  ? (userMetadata === null || userMetadata === void 0 ? void 0 : userMetadata.email) || fallbackEmail || username
218697
219283
  : t('chatHistoryChatHistoryTab.you');
218698
219284
  return (jsx("div", { className: "space-y-6", children: messages.map((message, index) => {
218699
- var _a, _b;
218700
- 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));
219285
+ var _a, _b, _c;
219286
+ 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 : '' }), jsx(TranscriptFileCards, { files: ((_b = message.human_files) !== null && _b !== void 0 ? _b : []).map((file) => ({
219287
+ name: file.name,
219288
+ contentType: file.content_type,
219289
+ url: file.url,
219290
+ })) })] })] }), 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: (_c = message.ai) !== null && _c !== void 0 ? _c : '' })] })] })] }, index));
218701
219291
  }) }));
218702
219292
  }
218703
219293
  /**
@@ -218802,6 +219392,607 @@ function ChatHistoryTab({ org, username }) {
218802
219392
  : 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 }) })] }));
218803
219393
  }
218804
219394
 
219395
+ /**
219396
+ * The info icon that sits on every analytics card and, on hover or keyboard
219397
+ * focus, explains what the card measures. The description doubles as the
219398
+ * accessible name so screen readers get it without needing the hover.
219399
+ *
219400
+ * Uses the tooltip primitives directly rather than the shared `TooltipContent`
219401
+ * because analytics wants a light popover, while `TooltipContent` is the dark
219402
+ * `bg-primary` style the rest of the app relies on.
219403
+ */
219404
+ function CardInfoIcon({ title, description, className }) {
219405
+ return (jsxs(Tooltip$1, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx("button", { type: "button", "aria-label": description, className: cn('shrink-0 cursor-help rounded p-1 text-[#898989] transition-colors hover:bg-gray-100 hover:text-[#38A1E5] focus-visible:ring-2 focus-visible:ring-[#38A1E5] focus-visible:outline-none', className), children: jsx(Info$3, { className: "h-4 w-4", "aria-hidden": "true" }) }) }), jsx(Portal$2, { children: jsxs(Content2$3, { "data-slot": "tooltip-content", side: "top", align: "center", sideOffset: 6, collisionPadding: 12, className: "animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 z-50 max-w-[260px] rounded-lg border border-[oklch(0.922_0_0)] bg-white p-3 shadow-lg", children: [title && (jsx("div", { "data-slot": "tooltip-title", className: "mb-1 text-sm font-semibold text-[#484848]", children: title })), jsx("p", { "data-slot": "tooltip-description", className: "text-sm leading-relaxed text-[#646464]", children: description })] }) })] }));
219406
+ }
219407
+
219408
+ /**
219409
+ * Chart color system for the analytics surface.
219410
+ *
219411
+ * Single-series charts keep the app's established `#38A1E5`. Multi-series
219412
+ * charts (spend stacked by service, spend by model) use a categorical palette
219413
+ * validated for color-vision-deficiency separation on a white surface
219414
+ * (worst adjacent-pair CVD ΔE 9.1, worst normal-vision ΔE 19.6 — OKLab ×100):
219415
+ * hues are assigned in this fixed order, per entity, never cycled or reshuffled
219416
+ * when a filter changes the series count.
219417
+ *
219418
+ * Three of the light-surface slots sit below 3:1 contrast against white, so
219419
+ * every chart that uses them must carry visible direct labels or a table view
219420
+ * (all of ours do — ranked rows and legends carry the values as text).
219421
+ */
219422
+ const CHART_SERIES_COLORS = [
219423
+ '#2a78d6', // blue
219424
+ '#eb6834', // orange
219425
+ '#1baf7a', // aqua
219426
+ '#eda100', // yellow
219427
+ '#e87ba4', // magenta
219428
+ '#008300', // green
219429
+ ];
219430
+ /**
219431
+ * Color follows the entity: a service keeps its hue whether or not the other
219432
+ * services are on screen. Services beyond this list fold into slots in first-
219433
+ * seen order via {@link colorForSeries} rather than inventing new hues.
219434
+ */
219435
+ const SERVICE_COLOR_ORDER = [
219436
+ 'chat',
219437
+ 'embeddings',
219438
+ 'memory',
219439
+ 'grading',
219440
+ 'moderation',
219441
+ 'call',
219442
+ ];
219443
+ /**
219444
+ * Stable color for a named series. Known services get their fixed slot; an
219445
+ * unknown name hashes into the palette deterministically so the same name is
219446
+ * the same color on every render and every chart.
219447
+ */
219448
+ function colorForSeries(name) {
219449
+ const fixed = SERVICE_COLOR_ORDER.indexOf(name);
219450
+ if (fixed >= 0)
219451
+ return CHART_SERIES_COLORS[fixed % CHART_SERIES_COLORS.length];
219452
+ let hash = 0;
219453
+ for (let i = 0; i < name.length; i++) {
219454
+ hash = (hash * 31 + name.charCodeAt(i)) >>> 0;
219455
+ }
219456
+ return CHART_SERIES_COLORS[hash % CHART_SERIES_COLORS.length];
219457
+ }
219458
+
219459
+ const LLM_USAGE_RANGES = ['today', '7d', '30d', '90d', 'all_time'];
219460
+ const DEFAULT_LLM_USAGE_RANGE = '30d';
219461
+ /** Buckets that keep the time axis readable for the chosen span. */
219462
+ function granularityForRange(range) {
219463
+ if (range === '90d')
219464
+ return 'week';
219465
+ if (range === 'all_time')
219466
+ return 'month';
219467
+ return 'day';
219468
+ }
219469
+ /** The next wider range, offered from the empty state. */
219470
+ function widerRange(range) {
219471
+ if (range === 'all_time')
219472
+ return null;
219473
+ if (range === '90d')
219474
+ return 'all_time';
219475
+ return '90d';
219476
+ }
219477
+ const DAY_MS = 86400000;
219478
+ const DAYS_IN_RANGE = { today: 1, '7d': 7, '30d': 30 };
219479
+ /**
219480
+ * Spend buckets for the chart. Daily ranges get every day of the window, with
219481
+ * zero where the backend sent no bucket (it omits empty days), so the axis is
219482
+ * continuous. Weekly and monthly ranges use the buckets as returned.
219483
+ */
219484
+ function buildSpendSeries(points, range, now = new Date()) {
219485
+ var _a;
219486
+ const days = DAYS_IN_RANGE[range];
219487
+ if (!days || granularityForRange(range) !== 'day')
219488
+ return points;
219489
+ const byDate = new Map(points.map((point) => [point.date, point.totalCost]));
219490
+ const series = [];
219491
+ for (let offset = days - 1; offset >= 0; offset--) {
219492
+ const date = new Date(now.getTime() - offset * DAY_MS).toISOString().slice(0, 10);
219493
+ series.push({ date, totalCost: (_a = byDate.get(date)) !== null && _a !== void 0 ? _a : 0 });
219494
+ }
219495
+ return series;
219496
+ }
219497
+ /** Axis ceiling and ticks on a 1-2-5 step, so labels land on clean numbers. */
219498
+ function niceScale(max, tickCount = 4) {
219499
+ if (!Number.isFinite(max) || max <= 0)
219500
+ return { max: 1, ticks: [0, 0.25, 0.5, 0.75, 1] };
219501
+ const rough = max / tickCount;
219502
+ const magnitude = 10 ** Math.floor(Math.log10(rough));
219503
+ const residual = rough / magnitude;
219504
+ const step = (residual <= 1 ? 1 : residual <= 2 ? 2 : residual <= 5 ? 5 : 10) * magnitude;
219505
+ const top = Math.ceil(max / step - 1e-9) * step;
219506
+ const ticks = [];
219507
+ for (let value = 0; value <= top + step / 2; value += step) {
219508
+ ticks.push(Number(value.toPrecision(12)));
219509
+ }
219510
+ return { max: ticks[ticks.length - 1], ticks };
219511
+ }
219512
+ const usd = (fractionDigits) => new Intl.NumberFormat('en-US', {
219513
+ style: 'currency',
219514
+ currency: 'USD',
219515
+ minimumFractionDigits: fractionDigits,
219516
+ maximumFractionDigits: fractionDigits,
219517
+ });
219518
+ /**
219519
+ * Dollars with enough precision to be meaningful: small values keep four
219520
+ * decimals. `precise` keeps four decimals for anything under a dollar — for
219521
+ * per-request figures (a trace, a step, the average), where $0.0147 and
219522
+ * $0.0102 must not both read as $0.01.
219523
+ */
219524
+ function formatCost(value, { precise = false } = {}) {
219525
+ if (value === null || value === undefined || !Number.isFinite(value))
219526
+ return '—';
219527
+ if (value === 0)
219528
+ return usd(2).format(0);
219529
+ const abs = Math.abs(value);
219530
+ if (abs < 0.0001)
219531
+ return `<${usd(4).format(0.0001)}`;
219532
+ if (abs < (precise ? 1 : 0.01))
219533
+ return usd(4).format(value);
219534
+ return usd(2).format(value);
219535
+ }
219536
+ /** Axis labels: whole dollars on big steps, cents (or finer) on small ones; zero is `$0`. */
219537
+ function formatAxisCost(value, step) {
219538
+ if (value === 0)
219539
+ return usd(0).format(0);
219540
+ const decimals = step >= 1 ? 0 : Math.min(4, Math.max(2, Math.ceil(-Math.log10(step))));
219541
+ return usd(decimals).format(value);
219542
+ }
219543
+ function formatCompact(value) {
219544
+ if (value === null || value === undefined || !Number.isFinite(value))
219545
+ return '—';
219546
+ return new Intl.NumberFormat('en-US', { notation: 'compact', maximumFractionDigits: 1 }).format(value);
219547
+ }
219548
+ function formatInteger(value) {
219549
+ if (value === null || value === undefined || !Number.isFinite(value))
219550
+ return '—';
219551
+ return Math.round(value).toLocaleString('en-US');
219552
+ }
219553
+ /** Trace and observation latency arrive in seconds. */
219554
+ function formatSeconds(value) {
219555
+ if (value === null || value === undefined || !Number.isFinite(value))
219556
+ return '—';
219557
+ return `${value < 10 ? value.toFixed(2) : value.toFixed(1)}s`;
219558
+ }
219559
+ /** `YYYY-MM-DD` bucket keys are UTC calendar dates. */
219560
+ function formatBucket(date, granularity) {
219561
+ const parsed = new Date(`${date.slice(0, 10)}T00:00:00Z`);
219562
+ if (Number.isNaN(parsed.getTime()))
219563
+ return date;
219564
+ return parsed.toLocaleDateString('en-US', granularity === 'month'
219565
+ ? { month: 'short', year: 'numeric', timeZone: 'UTC' }
219566
+ : { month: 'short', day: 'numeric', timeZone: 'UTC' });
219567
+ }
219568
+ function formatWhen(iso) {
219569
+ if (!iso)
219570
+ return '—';
219571
+ const date = new Date(iso);
219572
+ if (Number.isNaN(date.getTime()))
219573
+ return '—';
219574
+ return date.toLocaleString('en-US', {
219575
+ month: 'short',
219576
+ day: 'numeric',
219577
+ hour: '2-digit',
219578
+ minute: '2-digit',
219579
+ });
219580
+ }
219581
+ /** `0feb37e6-d125-…-7c9ee0a252e7` → `0feb…52e7`; the full id stays in a title. */
219582
+ function shortId(id) {
219583
+ if (!id)
219584
+ return '—';
219585
+ return id.length > 12 ? `${id.slice(0, 4)}…${id.slice(-4)}` : id;
219586
+ }
219587
+ /**
219588
+ * Keeps the biggest rows and folds the rest into one "Other" row, so a long
219589
+ * tail never floods the card.
219590
+ */
219591
+ function foldTail(rows, keep, otherLabel) {
219592
+ if (rows.length <= keep + 1)
219593
+ return rows;
219594
+ const head = rows.slice(0, keep);
219595
+ const tail = rows.slice(keep);
219596
+ return [
219597
+ ...head,
219598
+ {
219599
+ key: '__other__',
219600
+ label: otherLabel,
219601
+ totalCost: tail.reduce((sum, row) => sum + row.totalCost, 0),
219602
+ count: tail.reduce((sum, row) => sum + row.count, 0),
219603
+ isOther: true,
219604
+ },
219605
+ ];
219606
+ }
219607
+ /** Share of a total as a whole percent, keeping tiny non-zero shares visible. */
219608
+ function sharePercent(value, total) {
219609
+ if (!total || value <= 0)
219610
+ return 0;
219611
+ const pct = (value / total) * 100;
219612
+ return pct < 1 ? Math.round(pct * 10) / 10 : Math.round(pct);
219613
+ }
219614
+
219615
+ /** Single series, so the palette's lead hue; it clears 3:1 on white. */
219616
+ const BAR_COLOR = CHART_SERIES_COLORS[0];
219617
+ const PLOT_HEIGHT = 176;
219618
+ const MAX_X_LABELS = 5;
219619
+ /**
219620
+ * Spend per bucket as columns: ≤24px wide, 4px rounded tops, square at the
219621
+ * baseline, a hairline grid on clean ticks. Hover or arrow keys read one
219622
+ * column into the readout above the plot; the same numbers are in a
219623
+ * visually-hidden table for screen readers.
219624
+ */
219625
+ function SpendChart({ points, granularity, }) {
219626
+ const t = useT();
219627
+ const [active, setActive] = useState(null);
219628
+ const { max, ticks } = useMemo(() => niceScale(Math.max(0, ...points.map((point) => point.totalCost))), [points]);
219629
+ const step = ticks.length > 1 ? ticks[1] - ticks[0] : max;
219630
+ const labelIndexes = useMemo(() => {
219631
+ if (points.length <= MAX_X_LABELS)
219632
+ return new Set(points.map((_, index) => index));
219633
+ const every = (points.length - 1) / (MAX_X_LABELS - 1);
219634
+ return new Set(Array.from({ length: MAX_X_LABELS }, (_, i) => Math.round(i * every)));
219635
+ }, [points]);
219636
+ const onKeyDown = (event) => {
219637
+ if (!points.length)
219638
+ return;
219639
+ const last = points.length - 1;
219640
+ const current = active !== null && active !== void 0 ? active : last;
219641
+ let next = null;
219642
+ if (event.key === 'ArrowRight')
219643
+ next = Math.min(last, active === null ? last : current + 1);
219644
+ if (event.key === 'ArrowLeft')
219645
+ next = Math.max(0, active === null ? last : current - 1);
219646
+ if (event.key === 'Home')
219647
+ next = 0;
219648
+ if (event.key === 'End')
219649
+ next = last;
219650
+ if (next === null)
219651
+ return;
219652
+ event.preventDefault();
219653
+ setActive(next);
219654
+ };
219655
+ const activePoint = active !== null ? points[active] : null;
219656
+ return (jsxs("div", { className: "flex flex-col gap-2", children: [jsx("p", { className: "h-5 text-sm text-gray-600 dark:text-gray-300", "aria-live": "polite", children: activePoint && (jsxs(Fragment$1, { children: [jsx("span", { className: "font-semibold tabular-nums text-gray-900 dark:text-gray-100", children: formatCost(activePoint.totalCost) }), ' ', jsxs("span", { className: "text-gray-500 dark:text-gray-400", children: ["\u00B7 ", formatBucket(activePoint.date, granularity)] })] })) }), jsxs("div", { className: "flex gap-2", children: [jsx("div", { className: "relative w-12 flex-shrink-0 text-right text-[11px] tabular-nums text-gray-500 dark:text-gray-400", style: { height: PLOT_HEIGHT }, "aria-hidden": "true", children: ticks.map((tick) => (jsx("span", { className: "absolute right-0 -translate-y-1/2 leading-none", style: { top: `${(1 - tick / max) * 100}%` }, children: formatAxisCost(tick, step) }, tick))) }), jsxs("div", { className: "min-w-0 flex-1", children: [jsxs("div", { role: "group", tabIndex: 0, "aria-label": t('profileLlmUsage.chartAria'), onKeyDown: onKeyDown, onBlur: () => setActive(null), onMouseLeave: () => setActive(null), className: "relative rounded-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500", style: { height: PLOT_HEIGHT }, children: [ticks.map((tick) => (jsx("div", { className: tick === 0
219657
+ ? 'absolute inset-x-0 border-t border-gray-300 dark:border-gray-600'
219658
+ : 'absolute inset-x-0 border-t border-gray-100 dark:border-gray-800', style: { top: `${(1 - tick / max) * 100}%` }, "aria-hidden": "true" }, tick))), jsx("div", { className: "absolute inset-0 flex items-end gap-[2px]", "aria-hidden": "true", children: points.map((point, index) => {
219659
+ const height = max > 0 ? (point.totalCost / max) * 100 : 0;
219660
+ const isActive = index === active;
219661
+ return (jsx("div", {
219662
+ // The whole column slot is the hover target, not just the bar.
219663
+ className: "flex h-full min-w-0 flex-1 items-end justify-center", onMouseEnter: () => setActive(index), children: jsx("div", { className: "w-full max-w-[24px] rounded-t-[4px] transition-opacity", style: {
219664
+ height: point.totalCost > 0 ? `max(${height}%, 2px)` : 0,
219665
+ background: BAR_COLOR,
219666
+ opacity: active === null || isActive ? 1 : 0.45,
219667
+ } }) }, point.date));
219668
+ }) })] }), jsx("div", { className: "relative mt-2 h-4 text-[11px] text-gray-500 dark:text-gray-400", "aria-hidden": "true", children: points.map((point, index) => labelIndexes.has(index) ? (jsx("span", { className: "absolute whitespace-nowrap", style: index === 0
219669
+ ? { left: 0 }
219670
+ : index === points.length - 1
219671
+ ? { right: 0 }
219672
+ : {
219673
+ left: `${((index + 0.5) / points.length) * 100}%`,
219674
+ transform: 'translateX(-50%)',
219675
+ }, children: formatBucket(point.date, granularity) }, point.date)) : null) })] })] }), jsxs("table", { className: "sr-only", children: [jsx("caption", { children: t('profileLlmUsage.chartTableCaption') }), jsx("thead", { children: jsxs("tr", { children: [jsx("th", { scope: "col", children: t('profileLlmUsage.colPeriod') }), jsx("th", { scope: "col", children: t('profileLlmUsage.colCost') })] }) }), jsx("tbody", { children: points.map((point) => (jsxs("tr", { children: [jsx("td", { children: formatBucket(point.date, granularity) }), jsx("td", { children: formatCost(point.totalCost) })] }, point.date))) })] })] }));
219676
+ }
219677
+
219678
+ const RANGE_LABEL_KEYS = {
219679
+ today: 'rangeToday',
219680
+ '7d': 'range7d',
219681
+ '30d': 'range30d',
219682
+ '90d': 'range90d',
219683
+ all_time: 'rangeAllTime',
219684
+ };
219685
+ const PERIOD_LABEL_KEYS = {
219686
+ today: 'periodToday',
219687
+ '7d': 'period7d',
219688
+ '30d': 'period30d',
219689
+ '90d': 'period90d',
219690
+ all_time: 'periodAllTime',
219691
+ };
219692
+ const CHART_SUBTITLE_KEYS = {
219693
+ day: 'chartSubtitleDay',
219694
+ week: 'chartSubtitleWeek',
219695
+ month: 'chartSubtitleMonth',
219696
+ };
219697
+ const BREAKDOWN_ROWS = 5;
219698
+ const MODEL_BAR_COLOR = CHART_SERIES_COLORS[0];
219699
+ const CARD = 'rounded-xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-gray-950';
219700
+ function Card({ title, subtitle, aside, children, className, bodyClassName, }) {
219701
+ return (jsxs("section", { className: cn(CARD, className), "aria-label": title, children: [jsxs("div", { className: "flex items-start justify-between gap-4 px-5 pt-5", children: [jsxs("div", { className: "min-w-0", children: [jsx("h4", { className: "text-[15px] font-semibold text-gray-900 dark:text-gray-100", children: title }), subtitle && (jsx("p", { className: "mt-0.5 text-[13px] text-gray-500 dark:text-gray-400", children: subtitle }))] }), aside] }), jsx("div", { className: cn('px-5 pb-5 pt-4', bodyClassName), children: children })] }));
219702
+ }
219703
+ function RangeControl({ value, onChange, }) {
219704
+ const t = useT();
219705
+ const groupRef = useRef(null);
219706
+ const onKeyDown = (event, index) => {
219707
+ var _a, _b;
219708
+ const last = LLM_USAGE_RANGES.length - 1;
219709
+ let next = null;
219710
+ if (event.key === 'ArrowRight' || event.key === 'ArrowDown')
219711
+ next = index === last ? 0 : index + 1;
219712
+ if (event.key === 'ArrowLeft' || event.key === 'ArrowUp')
219713
+ next = index === 0 ? last : index - 1;
219714
+ if (next === null)
219715
+ return;
219716
+ event.preventDefault();
219717
+ onChange(LLM_USAGE_RANGES[next]);
219718
+ (_b = (_a = groupRef.current) === null || _a === void 0 ? void 0 : _a.querySelectorAll('[role="radio"]')[next]) === null || _b === void 0 ? void 0 : _b.focus();
219719
+ };
219720
+ return (jsx("div", { ref: groupRef, role: "radiogroup", "aria-label": t('profileLlmUsage.rangeLabel'), className: "inline-flex max-w-full gap-0.5 overflow-x-auto rounded-lg bg-gray-100 p-[3px] dark:bg-gray-800", children: LLM_USAGE_RANGES.map((range, index) => {
219721
+ const checked = range === value;
219722
+ return (jsx("button", { type: "button", role: "radio", "aria-checked": checked, tabIndex: checked ? 0 : -1, onClick: () => onChange(range), onKeyDown: (event) => onKeyDown(event, index), className: cn('h-8 whitespace-nowrap rounded-md px-3 text-[13px] transition-colors', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500', checked
219723
+ ? 'bg-white font-semibold text-gray-900 shadow-sm ring-1 ring-gray-200 dark:bg-gray-950 dark:text-gray-100 dark:ring-gray-700'
219724
+ : 'font-medium text-gray-600 hover:text-gray-900 dark:text-gray-300'), children: t(`profileLlmUsage.${RANGE_LABEL_KEYS[range]}`) }, range));
219725
+ }) }));
219726
+ }
219727
+ function StatTile({ label, value, sub, loading, }) {
219728
+ return (jsxs("div", { className: cn(CARD, 'flex flex-col gap-2 p-4'), children: [jsx("p", { className: "text-[13px] font-medium text-gray-500 dark:text-gray-400", children: label }), loading ? (jsxs(Fragment$1, { children: [jsx(Skeleton, { className: "h-7 w-24" }), jsx(Skeleton, { className: "h-4 w-20" })] })) : (jsxs(Fragment$1, { children: [jsx("p", { className: "text-[26px] font-semibold leading-tight tracking-tight text-gray-900 dark:text-gray-100", children: value }), jsx("p", { className: "min-h-[1rem] text-xs text-gray-500 tabular-nums dark:text-gray-400", children: sub })] }))] }));
219729
+ }
219730
+ function StateMessage({ icon, tone = 'neutral', title, body, action, }) {
219731
+ return (jsxs("div", { className: cn(CARD, 'flex flex-col items-center gap-3 px-6 py-14 text-center'), role: tone === 'error' ? 'alert' : undefined, children: [jsx("div", { className: cn('flex h-12 w-12 items-center justify-center rounded-xl', tone === 'error'
219732
+ ? 'bg-red-50 text-red-700 dark:bg-red-950 dark:text-red-300'
219733
+ : 'bg-gray-100 text-gray-500 dark:bg-gray-800 dark:text-gray-400'), "aria-hidden": "true", children: icon }), jsx("h4", { className: "text-base font-semibold text-gray-900 dark:text-gray-100", children: title }), jsx("p", { className: "max-w-sm text-sm leading-relaxed text-gray-500 dark:text-gray-400", children: body }), action] }));
219734
+ }
219735
+ function RankedBars({ rows, total, colorFor, }) {
219736
+ return (jsx("ul", { className: "flex flex-col gap-3", children: rows.map((row) => {
219737
+ const share = sharePercent(row.totalCost, total);
219738
+ return (jsxs("li", { className: "flex flex-col gap-1.5", children: [jsxs("div", { className: "flex items-baseline justify-between gap-3 text-[13px]", children: [jsxs("span", { className: "flex min-w-0 items-center gap-2 font-medium text-gray-900 dark:text-gray-100", children: [jsx("span", { className: "h-2 w-2 flex-shrink-0 rounded-[2px]", style: { background: colorFor(row) }, "aria-hidden": "true" }), jsx("span", { className: "truncate", title: row.label, children: row.label })] }), jsxs("span", { className: "whitespace-nowrap tabular-nums text-gray-700 dark:text-gray-300", children: [formatCost(row.totalCost), jsxs("span", { className: "text-gray-500 dark:text-gray-400", children: [" \u00B7 ", share, "%"] })] })] }), jsx("div", { className: "h-1.5 overflow-hidden rounded-full bg-gray-100 dark:bg-gray-800", "aria-hidden": "true", children: jsx("div", { className: "h-full rounded-full", style: { width: `${Math.min(100, share)}%`, background: colorFor(row) } }) })] }, row.key));
219739
+ }) }));
219740
+ }
219741
+ function BreakdownSkeleton() {
219742
+ return (jsx("div", { className: "flex flex-col gap-4", children: [0, 1, 2, 3].map((i) => (jsxs("div", { className: "flex flex-col gap-2", children: [jsx(Skeleton, { className: "h-4 w-3/4" }), jsx(Skeleton, { className: "h-1.5 w-full" })] }, i))) }));
219743
+ }
219744
+ function SectionError() {
219745
+ const t = useT();
219746
+ return (jsxs("p", { className: "flex items-center gap-2 text-sm text-gray-500 dark:text-gray-400", children: [jsx(CircleAlert, { className: "h-4 w-4 text-red-600", "aria-hidden": "true" }), t('profileLlmUsage.sectionError')] }));
219747
+ }
219748
+ const observationTokens = (o) => {
219749
+ var _a, _b, _c, _d, _e, _f;
219750
+ return ({
219751
+ input: (_b = (_a = o.usage) === null || _a === void 0 ? void 0 : _a.input) !== null && _b !== void 0 ? _b : o.promptTokens,
219752
+ output: (_d = (_c = o.usage) === null || _c === void 0 ? void 0 : _c.output) !== null && _d !== void 0 ? _d : o.completionTokens,
219753
+ total: (_f = (_e = o.usage) === null || _e === void 0 ? void 0 : _e.total) !== null && _f !== void 0 ? _f : o.totalTokens,
219754
+ });
219755
+ };
219756
+ const observationCost = (o) => { var _a, _b, _c; return (_b = (_a = o.calculatedTotalCost) !== null && _a !== void 0 ? _a : o.totalCost) !== null && _b !== void 0 ? _b : (_c = o.costDetails) === null || _c === void 0 ? void 0 : _c.total; };
219757
+ function TraceSteps({ steps, isLoading, error, }) {
219758
+ const t = useT();
219759
+ if (isLoading) {
219760
+ return (jsx("p", { className: "px-4 py-3 text-sm text-gray-500 dark:text-gray-400", children: t('profileLlmUsage.stepsLoading') }));
219761
+ }
219762
+ if (error) {
219763
+ return (jsx("p", { className: "px-4 py-3 text-sm text-gray-500 dark:text-gray-400", children: t('profileLlmUsage.stepsError') }));
219764
+ }
219765
+ if (!steps.length) {
219766
+ return (jsx("p", { className: "px-4 py-3 text-sm text-gray-500 dark:text-gray-400", children: t('profileLlmUsage.stepsEmpty') }));
219767
+ }
219768
+ const cell = 'px-3 py-2 text-right tabular-nums';
219769
+ return (jsx("div", { className: "overflow-x-auto", children: jsxs("table", { className: "w-full text-[12.5px]", children: [jsx("thead", { children: jsxs("tr", { className: "text-left text-gray-500 dark:text-gray-400", children: [jsx("th", { scope: "col", className: "px-4 py-2 font-medium", children: t('profileLlmUsage.colStep') }), jsx("th", { scope: "col", className: "px-3 py-2 font-medium", children: t('profileLlmUsage.colModel') }), jsx("th", { scope: "col", className: cn(cell, 'font-medium'), children: t('profileLlmUsage.colInput') }), jsx("th", { scope: "col", className: cn(cell, 'font-medium'), children: t('profileLlmUsage.colOutput') }), jsx("th", { scope: "col", className: cn(cell, 'font-medium'), children: t('profileLlmUsage.colTotal') }), jsx("th", { scope: "col", className: cn(cell, 'font-medium'), children: t('profileLlmUsage.colLatency') }), jsx("th", { scope: "col", className: "px-4 py-2 text-right font-medium", children: t('profileLlmUsage.colCost') })] }) }), jsx("tbody", { children: steps.map((step) => {
219770
+ const tokens = observationTokens(step);
219771
+ return (jsxs("tr", { className: "border-t border-gray-100 text-gray-700 dark:border-gray-800 dark:text-gray-300", children: [jsx("td", { className: "px-4 py-2 font-medium text-gray-900 dark:text-gray-100", children: step.name || step.type || '—' }), jsx("td", { className: "px-3 py-2", children: step.providedModelName || step.model || '—' }), jsx("td", { className: cell, children: formatInteger(tokens.input) }), jsx("td", { className: cell, children: formatInteger(tokens.output) }), jsx("td", { className: cell, children: formatInteger(tokens.total) }), jsx("td", { className: cell, children: formatSeconds(step.latency) }), jsx("td", { className: "px-4 py-2 text-right font-semibold tabular-nums text-gray-900 dark:text-gray-100", children: formatCost(observationCost(step), { precise: true }) })] }, step.id));
219772
+ }) })] }) }));
219773
+ }
219774
+ /**
219775
+ * The profile "Usage" tab: AI spend, tokens and requests for the profile on
219776
+ * screen. Every request names that user, so an admin viewing someone else
219777
+ * sees their rows rather than the whole platform's.
219778
+ */
219779
+ function LlmUsageTab({ org, username, isOwnProfile = true }) {
219780
+ var _a;
219781
+ const t = useT();
219782
+ const [range, setRange] = useState(DEFAULT_LLM_USAGE_RANGE);
219783
+ const [page, setPage] = useState(1);
219784
+ const [openTraceId, setOpenTraceId] = useState(null);
219785
+ const granularity = granularityForRange(range);
219786
+ const scope = { platformKey: org, username, date_filter: range };
219787
+ const summary = useUserLlmUsageSummary(scope);
219788
+ const spend = useUserLlmSpendOverTime({ ...scope, granularity });
219789
+ const byModel = useUserLlmCostByModel({ ...scope, limit: 20 });
219790
+ const byService = useUserLlmCostByService(scope);
219791
+ const activity = useUserLlmRecentActivity({
219792
+ ...scope,
219793
+ page,
219794
+ limit: USER_LLM_RECENT_ACTIVITY_PAGE_SIZE,
219795
+ });
219796
+ const steps = useUserLlmTraceObservations({ ...scope, traceId: openTraceId });
219797
+ const changeRange = (next) => {
219798
+ setRange(next);
219799
+ setPage(1);
219800
+ setOpenTraceId(null);
219801
+ };
219802
+ const refreshAll = () => {
219803
+ summary.refetch();
219804
+ spend.refetch();
219805
+ byModel.refetch();
219806
+ byService.refetch();
219807
+ activity.refetch();
219808
+ if (openTraceId)
219809
+ steps.refetch();
219810
+ };
219811
+ const series = useMemo(() => buildSpendSeries(spend.points, range), [spend.points, range]);
219812
+ const modelRows = useMemo(() => foldTail(byModel.models.map((row, index) => ({
219813
+ key: row.model || `unknown-${index}`,
219814
+ label: row.model || t('profileLlmUsage.unknownModel'),
219815
+ totalCost: row.totalCost,
219816
+ count: row.count,
219817
+ })), BREAKDOWN_ROWS, t('profileLlmUsage.otherRow')), [byModel.models, t]);
219818
+ const serviceRows = useMemo(() => foldTail(byService.services.map((row, index) => ({
219819
+ key: row.service || `unknown-${index}`,
219820
+ label: row.service || t('profileLlmUsage.unknownService'),
219821
+ totalCost: row.totalCost,
219822
+ count: row.count,
219823
+ })), BREAKDOWN_ROWS, t('profileLlmUsage.otherRow')), [byService.services, t]);
219824
+ const modelTotal = modelRows.reduce((sum, row) => sum + row.totalCost, 0);
219825
+ const serviceTotal = serviceRows.reduce((sum, row) => sum + row.totalCost, 0);
219826
+ const totals = summary.summary;
219827
+ const periodLabel = t(`profileLlmUsage.${PERIOD_LABEL_KEYS[range]}`);
219828
+ const next = widerRange(range);
219829
+ const errorCopy = (error) => {
219830
+ if (error.kind === 'unavailable') {
219831
+ return {
219832
+ title: t('profileLlmUsage.errorUnavailableTitle'),
219833
+ body: t('profileLlmUsage.errorUnavailableBody'),
219834
+ };
219835
+ }
219836
+ if (error.kind === 'unauthorized') {
219837
+ return {
219838
+ title: t('profileLlmUsage.errorUnauthorizedTitle'),
219839
+ body: t('profileLlmUsage.errorUnauthorizedBody'),
219840
+ };
219841
+ }
219842
+ if (error.kind === 'forbidden' || error.kind === 'not_found') {
219843
+ return {
219844
+ title: t('profileLlmUsage.errorForbiddenTitle'),
219845
+ body: t('profileLlmUsage.errorForbiddenBody'),
219846
+ };
219847
+ }
219848
+ return {
219849
+ title: t('profileLlmUsage.errorUnknownTitle'),
219850
+ body: t('profileLlmUsage.errorUnknownBody'),
219851
+ };
219852
+ };
219853
+ const meta = activity.meta;
219854
+ const pageSize = (meta === null || meta === void 0 ? void 0 : meta.limit) || USER_LLM_RECENT_ACTIVITY_PAGE_SIZE;
219855
+ const from = meta && meta.totalItems > 0 ? (meta.page - 1) * pageSize + 1 : 0;
219856
+ const to = meta ? Math.min(meta.page * pageSize, meta.totalItems) : 0;
219857
+ const totalPages = (_a = meta === null || meta === void 0 ? void 0 : meta.totalPages) !== null && _a !== void 0 ? _a : 1;
219858
+ let body;
219859
+ if (summary.error) {
219860
+ const copy = errorCopy(summary.error);
219861
+ body = (jsx(StateMessage, { tone: "error", icon: jsx(CircleAlert, { className: "h-6 w-6" }), title: copy.title, body: copy.body, action: summary.error.kind !== 'unauthorized' && (jsx(Button$1, { onClick: refreshAll, className: "mt-1 bg-blue-600 text-white hover:bg-blue-700", children: t('profileLlmUsage.tryAgain') })) }));
219862
+ }
219863
+ else if (summary.isEmpty) {
219864
+ body = (jsx(StateMessage, { icon: jsx(Inbox, { className: "h-6 w-6" }), title: t('profileLlmUsage.emptyTitle'), body: t('profileLlmUsage.emptyBody'), action: next && (jsx(Button$1, { variant: "outline", onClick: () => changeRange(next), className: "mt-1", children: next === 'all_time'
219865
+ ? t('profileLlmUsage.emptyActionAll')
219866
+ : t('profileLlmUsage.emptyAction90') })) }));
219867
+ }
219868
+ else {
219869
+ const loadingTotals = summary.isLoading || summary.isIdle;
219870
+ body = (jsxs(Fragment$1, { children: [jsxs("div", { className: "grid grid-cols-2 gap-3 xl:grid-cols-4", children: [jsx(StatTile, { label: t('profileLlmUsage.kpiSpend'), value: formatCost(totals === null || totals === void 0 ? void 0 : totals.totalCost), sub: periodLabel, loading: loadingTotals }), jsx(StatTile, { label: t('profileLlmUsage.kpiTokens'), value: formatCompact(totals === null || totals === void 0 ? void 0 : totals.totalTokens), sub: totals && totals.count > 0
219871
+ ? t('profileLlmUsage.kpiTokensSub', {
219872
+ value: formatCompact(totals.totalTokens / totals.count),
219873
+ })
219874
+ : undefined, loading: loadingTotals }), jsx(StatTile, { label: t('profileLlmUsage.kpiRequests'), value: formatInteger(totals === null || totals === void 0 ? void 0 : totals.count), sub: t('profileLlmUsage.kpiRequestsSub'), loading: loadingTotals }), jsx(StatTile, { label: t('profileLlmUsage.kpiAvgCost'), value: totals && totals.count > 0
219875
+ ? formatCost(totals.totalCost / totals.count, { precise: true })
219876
+ : '—', sub: t('profileLlmUsage.kpiAvgCostSub'), loading: loadingTotals })] }), jsx(Card, { title: t('profileLlmUsage.chartTitle'), subtitle: t(`profileLlmUsage.${CHART_SUBTITLE_KEYS[granularity]}`), aside: totals && (jsx("span", { className: "text-[15px] font-semibold tabular-nums text-gray-900 dark:text-gray-100", children: formatCost(totals.totalCost) })), bodyClassName: "pt-2", children: spend.isLoading || spend.isIdle ? (jsx(Skeleton, { className: "h-[220px] w-full" })) : spend.error ? (jsx(SectionError, {})) : series.length === 0 ? (jsx("p", { className: "py-10 text-center text-sm text-gray-500 dark:text-gray-400", children: t('profileLlmUsage.chartNoData') })) : (jsx(SpendChart, { points: series, granularity: granularity })) }), jsxs("div", { className: "grid grid-cols-1 gap-3 md:grid-cols-2", children: [jsx(Card, { title: t('profileLlmUsage.byModelTitle'), subtitle: t('profileLlmUsage.byModelSubtitle'), children: byModel.isLoading || byModel.isIdle ? (jsx(BreakdownSkeleton, {})) : byModel.error ? (jsx(SectionError, {})) : modelRows.length === 0 ? (jsx("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: t('profileLlmUsage.breakdownEmpty') })) : (jsx(RankedBars, { rows: modelRows, total: modelTotal, colorFor: () => MODEL_BAR_COLOR })) }), jsx(Card, { title: t('profileLlmUsage.byServiceTitle'), subtitle: t('profileLlmUsage.byServiceSubtitle'), children: byService.isLoading || byService.isIdle ? (jsx(BreakdownSkeleton, {})) : byService.error ? (jsx(SectionError, {})) : serviceRows.length === 0 ? (jsx("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: t('profileLlmUsage.breakdownEmpty') })) : (jsx(RankedBars, { rows: serviceRows, total: serviceTotal,
219877
+ // Colour follows the service, so "chat" is the same hue everywhere.
219878
+ colorFor: (row) => (row.isOther ? '#9ca3af' : colorForSeries(row.key)) })) })] }), jsxs("section", { className: cn(CARD, 'overflow-hidden'), "aria-labelledby": "llm-activity-heading", children: [jsxs("div", { className: "border-b border-gray-200 px-5 py-4 dark:border-gray-800", children: [jsx("h4", { id: "llm-activity-heading", className: "text-[15px] font-semibold text-gray-900 dark:text-gray-100", children: t('profileLlmUsage.activityTitle') }), jsx("p", { className: "mt-0.5 text-[13px] text-gray-500 dark:text-gray-400", children: t('profileLlmUsage.activitySubtitle') })] }), activity.isLoading || activity.isIdle ? (jsx("div", { className: "flex flex-col gap-3 p-5", children: [0, 1, 2, 3].map((i) => (jsx(Skeleton, { className: "h-8 w-full" }, i))) })) : activity.error ? (jsx("div", { className: "p-5", children: jsx(SectionError, {}) })) : activity.isEmpty || activity.rows.length === 0 ? (jsx("p", { className: "p-5 text-sm text-gray-500 dark:text-gray-400", children: t('profileLlmUsage.activityEmpty') })) : (jsxs(Fragment$1, { children: [jsx("div", { className: "overflow-x-auto", children: jsxs("table", { className: "w-full text-[13px]", children: [jsx("thead", { children: jsxs("tr", { className: "bg-gray-50 text-left text-gray-500 dark:bg-gray-900 dark:text-gray-400", children: [jsx("th", { scope: "col", className: "px-5 py-2.5 font-medium", children: t('profileLlmUsage.colWhen') }), jsx("th", { scope: "col", className: "px-3 py-2.5 font-medium", children: t('profileLlmUsage.colService') }), jsx("th", { scope: "col", className: "hidden px-3 py-2.5 font-medium md:table-cell", children: t('profileLlmUsage.colSession') }), jsx("th", { scope: "col", className: "px-3 py-2.5 text-right font-medium", children: t('profileLlmUsage.colLatency') }), jsx("th", { scope: "col", className: "px-3 py-2.5 text-right font-medium", children: t('profileLlmUsage.colCost') }), jsx("th", { scope: "col", className: "w-12 py-2.5 pl-2 pr-5", children: jsx("span", { className: "sr-only", children: t('profileLlmUsage.colDetails') }) })] }) }), jsx("tbody", { children: activity.rows.map((trace) => {
219879
+ const open = trace.id === openTraceId;
219880
+ const when = formatWhen(trace.timestamp);
219881
+ const service = trace.name || t('profileLlmUsage.unknownService');
219882
+ const stepsId = `llm-trace-${trace.id}-steps`;
219883
+ return (jsxs(Fragment$2, { children: [jsxs("tr", { className: cn('border-t border-gray-200 dark:border-gray-800', open && 'bg-gray-50 dark:bg-gray-900'), children: [jsx("td", { className: "whitespace-nowrap px-5 py-3 text-gray-900 dark:text-gray-100", children: when }), jsx("td", { className: "px-3 py-3", children: jsxs("span", { className: "inline-flex items-center gap-2 text-gray-700 dark:text-gray-300", children: [jsx("span", { className: "h-2 w-2 flex-shrink-0 rounded-[2px]", style: { background: colorForSeries(trace.name || '') }, "aria-hidden": "true" }), jsx("span", { className: "max-w-[16rem] truncate", title: service, children: service })] }) }), jsx("td", { className: "hidden px-3 py-3 font-mono text-xs text-gray-500 dark:text-gray-400 md:table-cell", title: trace.sessionId, children: shortId(trace.sessionId) }), jsx("td", { className: "px-3 py-3 text-right tabular-nums text-gray-700 dark:text-gray-300", children: formatSeconds(trace.latency) }), jsx("td", { className: "px-3 py-3 text-right font-semibold tabular-nums text-gray-900 dark:text-gray-100", children: formatCost(trace.totalCost, { precise: true }) }), jsx("td", { className: "py-2 pl-2 pr-5", children: jsx("button", { type: "button", "aria-expanded": open, "aria-controls": open ? stepsId : undefined, "aria-label": open
219884
+ ? t('profileLlmUsage.hideSteps', { when })
219885
+ : t('profileLlmUsage.showSteps', { when }), onClick: () => setOpenTraceId(open ? null : trace.id), className: cn('flex h-8 w-8 items-center justify-center rounded-md transition-colors', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500', open
219886
+ ? 'bg-gray-200 text-gray-900 dark:bg-gray-700 dark:text-gray-100'
219887
+ : 'text-gray-500 hover:bg-gray-100 hover:text-gray-900 dark:hover:bg-gray-800'), children: open ? (jsx(ChevronDown, { className: "h-4 w-4", "aria-hidden": "true" })) : (jsx(ChevronRight, { className: "h-4 w-4", "aria-hidden": "true" })) }) })] }), open && (jsx("tr", { className: "bg-gray-50 dark:bg-gray-900", children: jsx("td", { colSpan: 6, className: "px-5 pb-4", children: jsx("div", { id: stepsId, className: "overflow-hidden rounded-lg border border-gray-200 bg-white dark:border-gray-800 dark:bg-gray-950", children: jsx(TraceSteps, { steps: steps.rows, isLoading: steps.isLoading, error: steps.error }) }) }) }))] }, trace.id));
219888
+ }) })] }) }), meta && (jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 border-t border-gray-200 px-5 py-3 dark:border-gray-800", children: [jsx("p", { className: "text-[13px] tabular-nums text-gray-500 dark:text-gray-400", children: t('profileLlmUsage.paginationSummary', {
219889
+ from: formatInteger(from),
219890
+ to: formatInteger(to),
219891
+ total: formatInteger(meta.totalItems),
219892
+ }) }), jsxs("div", { className: "flex gap-2", children: [jsxs(Button$1, { variant: "outline", size: "sm", className: "h-8 gap-1", disabled: page <= 1 || activity.isFetching, onClick: () => {
219893
+ setOpenTraceId(null);
219894
+ setPage((current) => Math.max(1, current - 1));
219895
+ }, children: [jsx(ChevronLeft, { className: "h-4 w-4", "aria-hidden": "true" }), t('profileLlmUsage.previous')] }), jsxs(Button$1, { variant: "outline", size: "sm", className: "h-8 gap-1", disabled: page >= totalPages || activity.isFetching, onClick: () => {
219896
+ setOpenTraceId(null);
219897
+ setPage((current) => current + 1);
219898
+ }, children: [t('profileLlmUsage.next'), jsx(ChevronRight, { className: "h-4 w-4", "aria-hidden": "true" })] })] })] }))] }))] })] }));
219899
+ }
219900
+ const isRefreshing = summary.isFetching ||
219901
+ spend.isFetching ||
219902
+ byModel.isFetching ||
219903
+ byService.isFetching ||
219904
+ activity.isFetching;
219905
+ return (jsxs("div", { className: "flex flex-col gap-5", "data-testid": "llm-usage-tab", children: [jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [jsx(RangeControl, { value: range, onChange: changeRange }), jsxs("div", { className: "flex items-center gap-3", children: [jsxs("span", { className: "inline-flex items-center gap-1.5 text-[13px] text-gray-500 dark:text-gray-400", children: [jsx(Lock, { className: "h-3.5 w-3.5", "aria-hidden": "true" }), isOwnProfile
219906
+ ? t('profileLlmUsage.scopeSelf')
219907
+ : t('profileLlmUsage.scopeUser', { username })] }), jsx(Button$1, { variant: "outline", size: "icon", className: "h-9 w-9", onClick: refreshAll, disabled: isRefreshing, "aria-label": t('profileLlmUsage.refresh'), title: t('profileLlmUsage.refresh'), children: jsx(RefreshCw, { className: cn('h-4 w-4', isRefreshing && 'animate-spin'), "aria-hidden": "true" }) })] })] }), body] }));
219908
+ }
219909
+
219910
+ const tabId = (id, mobile) => (mobile ? `${id}-tab-mobile` : `${id}-tab`);
219911
+ /** Arrow/Home/End handling for the tab variant, with automatic activation. */
219912
+ function useTabKeyboard(items, onSelect, mobile) {
219913
+ const containerRef = useRef(null);
219914
+ const onKeyDown = (event, index) => {
219915
+ var _a, _b;
219916
+ const last = items.length - 1;
219917
+ let next = null;
219918
+ if (event.key === 'ArrowDown' || event.key === 'ArrowRight')
219919
+ next = index === last ? 0 : index + 1;
219920
+ if (event.key === 'ArrowUp' || event.key === 'ArrowLeft')
219921
+ next = index === 0 ? last : index - 1;
219922
+ if (event.key === 'Home')
219923
+ next = 0;
219924
+ if (event.key === 'End')
219925
+ next = last;
219926
+ if (next === null)
219927
+ return;
219928
+ event.preventDefault();
219929
+ const target = items[next];
219930
+ onSelect(target.id);
219931
+ (_b = (_a = containerRef.current) === null || _a === void 0 ? void 0 : _a.querySelector(`[id="${tabId(target.id, mobile)}"]`)) === null || _b === void 0 ? void 0 : _b.focus();
219932
+ };
219933
+ return { containerRef, onKeyDown };
219934
+ }
219935
+ /** Grouped vertical navigation for the desktop sidebar. */
219936
+ function SettingsSidebarNav({ groups, activeId, onSelect, variant, ariaLabel, }) {
219937
+ const visibleGroups = groups.filter((group) => group.items.length > 0);
219938
+ const flat = visibleGroups.flatMap((group) => group.items);
219939
+ const isTabs = variant === 'tabs';
219940
+ const { containerRef, onKeyDown } = useTabKeyboard(flat, onSelect, false);
219941
+ const renderItem = (item) => {
219942
+ const active = item.id === activeId;
219943
+ const Icon = item.icon;
219944
+ return (jsxs("button", { type: "button", onClick: () => onSelect(item.id), ...(isTabs
219945
+ ? {
219946
+ role: 'tab',
219947
+ id: tabId(item.id, false),
219948
+ 'aria-selected': active,
219949
+ 'aria-controls': `${item.id}-tabpanel`,
219950
+ tabIndex: active ? 0 : -1,
219951
+ onKeyDown: (event) => onKeyDown(event, flat.indexOf(item)),
219952
+ }
219953
+ : { 'aria-current': active ? 'page' : undefined }), className: cn('group flex h-9 w-full items-center gap-2.5 rounded-lg px-2.5 text-left text-sm transition-colors', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-1', active
219954
+ ? 'bg-white font-semibold text-gray-900 shadow-sm ring-1 ring-gray-200 dark:bg-gray-800 dark:text-gray-100 dark:ring-gray-700'
219955
+ : 'font-medium text-gray-700 hover:bg-gray-100 hover:text-gray-900 dark:text-gray-300 dark:hover:bg-gray-800 dark:hover:text-gray-100'), children: [jsx(Icon, { className: cn('h-4 w-4 flex-shrink-0', active
219956
+ ? 'text-blue-600 dark:text-blue-400'
219957
+ : 'text-gray-500 group-hover:text-gray-700 dark:text-gray-400'), "aria-hidden": true }), jsx("span", { className: "truncate", children: item.label })] }, item.id));
219958
+ };
219959
+ return (jsx("nav", { className: "min-h-0 flex-1 overflow-y-auto px-3 py-4", "aria-label": ariaLabel, children: jsx("div", { ref: containerRef, className: "flex flex-col gap-4", ...(isTabs ? { role: 'tablist', 'aria-orientation': 'vertical' } : {}), children: visibleGroups.map((group) => (jsxs("div", { className: "flex flex-col gap-0.5", ...(isTabs
219960
+ ? {}
219961
+ : { role: 'group', 'aria-labelledby': `settings-nav-group-${group.id}` }), children: [jsx("div", { id: `settings-nav-group-${group.id}`, className: "px-2.5 pb-1 text-[11px] font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400", "aria-hidden": isTabs ? true : undefined, children: group.label }), group.items.map(renderItem)] }, group.id))) }) }));
219962
+ }
219963
+ /** Horizontally scrolling pills for small screens. */
219964
+ function SettingsMobileNav({ groups, activeId, onSelect, variant, ariaLabel, }) {
219965
+ const flat = groups.flatMap((group) => group.items);
219966
+ const isTabs = variant === 'tabs';
219967
+ const { containerRef, onKeyDown } = useTabKeyboard(flat, onSelect, true);
219968
+ return (jsx("nav", { className: "border-b border-gray-200 dark:border-gray-800 lg:hidden", "aria-label": ariaLabel, children: jsx("div", { ref: containerRef, className: "flex gap-2 overflow-x-auto px-4 py-2.5 [scrollbar-width:none] sm:px-6", ...(isTabs ? { role: 'tablist', 'aria-orientation': 'horizontal' } : {}), children: flat.map((item, index) => {
219969
+ const active = item.id === activeId;
219970
+ const Icon = item.icon;
219971
+ return (jsxs("button", { type: "button", onClick: () => onSelect(item.id), ...(isTabs
219972
+ ? {
219973
+ role: 'tab',
219974
+ id: tabId(item.id, true),
219975
+ 'aria-selected': active,
219976
+ 'aria-controls': `${item.id}-tabpanel`,
219977
+ tabIndex: active ? 0 : -1,
219978
+ onKeyDown: (event) => onKeyDown(event, index),
219979
+ }
219980
+ : { 'aria-current': active ? 'page' : undefined }), className: cn('flex h-9 flex-shrink-0 items-center gap-1.5 whitespace-nowrap rounded-full px-3.5 text-sm transition-colors', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-1', active
219981
+ ? 'bg-blue-600 font-semibold text-white dark:bg-blue-500 dark:text-white'
219982
+ : 'border border-gray-200 bg-white font-medium text-gray-700 hover:bg-gray-50 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300'), children: [jsx(Icon, { className: "h-3.5 w-3.5", "aria-hidden": true }), jsx("span", { children: item.label })] }, item.id));
219983
+ }) }) }));
219984
+ }
219985
+ /** Small uppercase chip under the identity card (role, platform key). */
219986
+ function SettingsChip({ children, tone = 'neutral', icon: Icon, }) {
219987
+ return (jsxs("span", { className: cn('inline-flex items-center gap-1 rounded-md px-2 py-0.5 text-xs font-semibold', tone === 'accent'
219988
+ ? 'bg-blue-50 text-blue-700 dark:bg-blue-950 dark:text-blue-300'
219989
+ : 'bg-gray-100 text-gray-600 tracking-wide dark:bg-gray-800 dark:text-gray-300'), children: [Icon && jsx(Icon, { className: "h-3 w-3", "aria-hidden": true }), children] }));
219990
+ }
219991
+ function SettingsShell({ sidebarHeader, sidebarNav, mobileHeader, mobileNav, title, description, panelProps, footer, overlays, children, }) {
219992
+ const { className: panelClassName, ...restPanelProps } = panelProps !== null && panelProps !== void 0 ? panelProps : {};
219993
+ return (jsxs("div", { className: "flex h-full min-h-0 flex-col bg-white dark:bg-gray-950 lg:flex-row", children: [jsx("div", { className: "border-b border-gray-200 py-3 pl-4 pr-14 dark:border-gray-800 sm:pl-6 lg:hidden", children: mobileHeader }), mobileNav, jsxs("aside", { className: "hidden h-full w-[280px] flex-shrink-0 flex-col overflow-hidden border-r border-gray-200 bg-gray-50 dark:border-gray-800 dark:bg-gray-900 lg:flex", children: [jsx("div", { className: "flex-shrink-0 border-b border-gray-200 px-5 pb-4 pt-6 dark:border-gray-800", children: sidebarHeader }), sidebarNav] }), jsxs("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden", children: [jsxs("div", { className: "hidden flex-shrink-0 border-b border-gray-200 py-5 pl-8 pr-16 dark:border-gray-800 lg:block", children: [jsx("h3", { className: "text-xl font-semibold tracking-tight text-gray-900 dark:text-gray-100", children: title }), description && (jsx("p", { className: "mt-1 text-sm text-gray-500 dark:text-gray-400", children: description }))] }), jsxs("div", { className: cn('min-h-0 flex-1 overflow-y-auto overflow-x-hidden px-4 py-5 sm:px-6 lg:px-8 lg:py-6', panelClassName), ...restPanelProps, children: [jsxs("div", { className: "mb-5 lg:hidden", children: [jsx("h3", { className: "text-lg font-semibold text-gray-900 dark:text-gray-100", children: title }), description && (jsx("p", { className: "mt-1 text-sm text-gray-500 dark:text-gray-400", children: description }))] }), children] }), footer && (jsx("div", { className: "flex-shrink-0 border-t border-gray-200 bg-white px-4 py-3 dark:border-gray-800 dark:bg-gray-950 sm:px-6 lg:px-8 lg:py-4", children: footer }))] }), overlays] }));
219994
+ }
219995
+
218805
219996
  const COWORK_ENABLED_KEY = 'ibl_cowork_enabled';
218806
219997
  /**
218807
219998
  * Whether Cowork is enabled, read from localStorage. This is a user preference
@@ -218822,10 +220013,7 @@ function setCoworkEnabled(enabled) {
218822
220013
  localStorage.setItem(COWORK_ENABLED_KEY, String(enabled));
218823
220014
  }
218824
220015
 
218825
- const renderLucideIcon = (Icon) => function RenderedIcon(props) {
218826
- return jsx(Icon, { ...props });
218827
- };
218828
- const TAB_DESC_KEYS = {
220016
+ const TAB_DESC_KEYS$1 = {
218829
220017
  basic: 'descBasic',
218830
220018
  social: 'descSocial',
218831
220019
  education: 'descEducation',
@@ -218835,6 +220023,7 @@ const TAB_DESC_KEYS = {
218835
220023
  memory: 'descMemory',
218836
220024
  privacy: 'descPrivacy',
218837
220025
  chatHistory: 'descChatHistory',
220026
+ llmUsage: 'descLlmUsage',
218838
220027
  security: 'descSecurity',
218839
220028
  advanced: 'descAdvanced',
218840
220029
  };
@@ -218849,9 +220038,13 @@ const TAB_DESC_PREVIEW_KEYS = {
218849
220038
  memory: 'descMemoryPreview',
218850
220039
  privacy: 'descPrivacyPreview',
218851
220040
  chatHistory: 'descChatHistoryPreview',
220041
+ llmUsage: 'descLlmUsagePreview',
218852
220042
  };
218853
- function Profile({ tenant, username, tenants, onClose, customization = {}, isAdmin = false, targetTab = 'basic', onAccountDeleted, enableMemoryTab = false, localLLMProps, currentSPA = '', }) {
218854
- var _a, _b, _c, _d, _e, _f, _g, _h;
220043
+ const LABEL_CLASS = 'text-sm font-medium text-gray-700 dark:text-gray-300';
220044
+ const FIELD_CLASS = 'h-10 w-full bg-white border-gray-300 text-sm text-gray-900 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-900 dark:border-gray-700 dark:text-gray-100';
220045
+ const PRIMARY_BUTTON_CLASS = 'bg-blue-600 text-white hover:bg-blue-700 focus-visible:ring-blue-500 disabled:bg-blue-600/60';
220046
+ function Profile({ tenant, username, tenants, onClose, customization = {}, isAdmin = false, targetTab = 'basic', onAccountDeleted, enableMemoryTab = false, showLlmUsageTab = true, localLLMProps, currentSPA = '', }) {
220047
+ var _a, _b, _c, _d, _e, _f;
218855
220048
  const t = useT();
218856
220049
  const { data: memsearchStatus, isError: isMemsearchStatusError } = useGetMemsearchStatusQuery({ org: tenant, userId: username }, { skip: !enableMemoryTab });
218857
220050
  // Show memory tab if enabled for this platform AND memsearch is enabled (or status API is unavailable)
@@ -218870,34 +220063,46 @@ function Profile({ tenant, username, tenants, onClose, customization = {}, isAdm
218870
220063
  const rbacPermissions = useSelector(selectRbacPermissions);
218871
220064
  const { currentTenant: viewerTenant } = useCurrentTenant();
218872
220065
  const isChatHistoryVisible = !isReadOnly || canViewOtherUsersChatHistory(rbacPermissions, viewerTenant);
218873
- const baseTabs = [
218874
- { id: 'basic', label: t('profileIndex.tabBasic'), renderIcon: renderLucideIcon(User) },
218875
- { id: 'social', label: t('profileIndex.tabSocial'), renderIcon: renderLucideIcon(Globe) },
220066
+ // Usage is always read for the profile on screen, so it works while
220067
+ // previewing someone else too — the backend decides whether this viewer may
220068
+ // see their rows, and answers 403 when they may not. A pure watcher (the
220069
+ // `/watchedgroups/#list` permission without tenant admin) is kept out of
220070
+ // cost reporting entirely, the same way the analytics tabs exclude them.
220071
+ const isLlmUsageVisible = showLlmUsageTab && !isPureWatcher(rbacPermissions, viewerTenant);
220072
+ // Add Advanced tab only when in Tauri app (Local Models is provided and available)
220073
+ const isAdvancedAvailable = (localLLMProps === null || localLLMProps === void 0 ? void 0 : localLLMProps.isAvailable) === true;
220074
+ const TABS = [
220075
+ { id: 'basic', label: t('profileIndex.tabBasic'), icon: User, group: 'profile' },
220076
+ { id: 'social', label: t('profileIndex.tabSocial'), icon: Globe, group: 'profile' },
218876
220077
  ...(isGradebookEnabled
218877
220078
  ? [
218878
220079
  {
218879
220080
  id: 'gradebook',
218880
220081
  label: t('profileIndex.tabGradebook'),
218881
- renderIcon: renderLucideIcon(GraduationCap),
220082
+ icon: GraduationCap,
220083
+ group: 'records',
218882
220084
  },
218883
220085
  ]
218884
220086
  : []),
218885
220087
  {
218886
220088
  id: 'education',
218887
220089
  label: t('profileIndex.tabEducation'),
218888
- renderIcon: renderLucideIcon(BookOpen),
220090
+ icon: BookOpen,
220091
+ group: 'records',
218889
220092
  },
218890
220093
  {
218891
220094
  id: 'experience',
218892
220095
  label: t('profileIndex.tabExperience'),
218893
- renderIcon: renderLucideIcon(Briefcase),
220096
+ icon: Briefcase,
220097
+ group: 'records',
218894
220098
  },
218895
220099
  ...(isPurchasesEnabled
218896
220100
  ? [
218897
220101
  {
218898
220102
  id: 'purchases',
218899
220103
  label: t('profileIndex.tabPurchases'),
218900
- renderIcon: renderLucideIcon(ShoppingBag),
220104
+ icon: ShoppingBag,
220105
+ group: 'records',
218901
220106
  },
218902
220107
  ]
218903
220108
  : []),
@@ -218906,20 +220111,23 @@ function Profile({ tenant, username, tenants, onClose, customization = {}, isAdm
218906
220111
  {
218907
220112
  id: 'memory',
218908
220113
  label: t('profileIndex.tabMemory'),
218909
- renderIcon: renderLucideIcon(Archive),
220114
+ icon: Archive,
220115
+ group: 'aiData',
218910
220116
  },
218911
220117
  ]
218912
220118
  : []),
218913
220119
  // id stays as 'privacy' so callers passing `targetTab='privacy'` and
218914
220120
  // any test selectors targeting #panel-privacy keep working. The
218915
220121
  // user-visible `label` is "Privacy"; the chat-privacy feature itself
218916
- // is still called "Private Mode" within the tab's content.
220122
+ // is still called "Private Mode" within the tab's content. It sits with
220123
+ // the account controls, just above Security.
218917
220124
  ...(isChatPrivacyEnabled
218918
220125
  ? [
218919
220126
  {
218920
220127
  id: 'privacy',
218921
220128
  label: t('profileIndex.tabPrivacy'),
218922
- renderIcon: renderLucideIcon(Lock),
220129
+ icon: Lock,
220130
+ group: 'account',
218923
220131
  },
218924
220132
  ]
218925
220133
  : []),
@@ -218928,7 +220136,18 @@ function Profile({ tenant, username, tenants, onClose, customization = {}, isAdm
218928
220136
  {
218929
220137
  id: 'chatHistory',
218930
220138
  label: t('profileIndex.tabChatHistory'),
218931
- renderIcon: renderLucideIcon(History),
220139
+ icon: History,
220140
+ group: 'aiData',
220141
+ },
220142
+ ]
220143
+ : []),
220144
+ ...(isLlmUsageVisible
220145
+ ? [
220146
+ {
220147
+ id: 'llmUsage',
220148
+ label: t('profileIndex.tabLlmUsage'),
220149
+ icon: Gauge,
220150
+ group: 'aiData',
218932
220151
  },
218933
220152
  ]
218934
220153
  : []),
@@ -218937,24 +220156,33 @@ function Profile({ tenant, username, tenants, onClose, customization = {}, isAdm
218937
220156
  {
218938
220157
  id: 'security',
218939
220158
  label: t('profileIndex.tabSecurity'),
218940
- renderIcon: renderLucideIcon(Shield),
220159
+ icon: Shield,
220160
+ group: 'account',
220161
+ },
220162
+ ]
220163
+ : []),
220164
+ ...(isAdvancedAvailable
220165
+ ? [
220166
+ {
220167
+ id: 'advanced',
220168
+ label: t('profileIndex.tabAdvanced'),
220169
+ icon: Settings$1,
220170
+ group: 'account',
218941
220171
  },
218942
220172
  ]
218943
220173
  : []),
218944
220174
  ];
218945
- // Add Advanced tab only when in Tauri app (Local Models is provided and available)
218946
- const isAdvancedAvailable = (localLLMProps === null || localLLMProps === void 0 ? void 0 : localLLMProps.isAvailable) === true;
218947
- const TABS = isAdvancedAvailable
218948
- ? [
218949
- ...baseTabs,
218950
- {
218951
- id: 'advanced',
218952
- label: t('profileIndex.tabAdvanced'),
218953
- renderIcon: renderLucideIcon(Settings$1),
218954
- },
218955
- ]
218956
- : baseTabs;
218957
- const RoleIcon = renderLucideIcon(User);
220175
+ const GROUP_LABELS = {
220176
+ profile: t('profileIndex.groupProfile'),
220177
+ records: t('profileIndex.groupRecords'),
220178
+ aiData: t('profileIndex.groupAiData'),
220179
+ account: t('profileIndex.groupAccount'),
220180
+ };
220181
+ const navGroups = Object.keys(GROUP_LABELS).map((group) => ({
220182
+ id: group,
220183
+ label: GROUP_LABELS[group],
220184
+ items: TABS.filter((tab) => tab.group === group),
220185
+ }));
218958
220186
  const [activeTab, setActiveTab] = useState(targetTab);
218959
220187
  const [isHovering, setIsHovering] = useState(false);
218960
220188
  const fileInputRef = useRef(null);
@@ -218976,8 +220204,7 @@ function Profile({ tenant, username, tenants, onClose, customization = {}, isAdm
218976
220204
  };
218977
220205
  // The picture itself, with no editing affordance around it. The wrapper
218978
220206
  // below decides whether it is clickable.
218979
- const profileAvatar = (jsxs(Avatar, { className: "h-20 w-20", children: [((_b = userMetadataEdx === null || userMetadataEdx === void 0 ? void 0 : userMetadataEdx.profile_image) === null || _b === void 0 ? void 0 : _b.has_image) ? (jsx(AvatarImage, { src: (_c = userMetadataEdx === null || userMetadataEdx === void 0 ? void 0 : userMetadataEdx.profile_image) === null || _c === void 0 ? void 0 : _c.image_url_full, alt: t('profileIndex.altProfilePicture'), className: "rounded-full object-cover w-full h-full group-hover:opacity-80 transition-opacity" })) : customization.useGravatarPicFallback ? (jsx(Gravatar, { className: "w-full h-full rounded-full object-cover group-hover:opacity-80 transition-opacity", email: userMetadata === null || userMetadata === void 0 ? void 0 : userMetadata.email, size: 80, alt: t('profileIndex.altProfilePictureGravatar') })) : (jsx(Fragment$1, {})), jsx(AvatarFallback, { className: cn('bg-blue-800 text-white text-xl rounded-full', customization.useGravatarPicFallback &&
218980
- !((_d = userMetadataEdx === null || userMetadataEdx === void 0 ? void 0 : userMetadataEdx.profile_image) === null || _d === void 0 ? void 0 : _d.image_url_full)
220207
+ const profileAvatar = (jsxs(Avatar, { className: "h-14 w-14", children: [((_b = userMetadataEdx === null || userMetadataEdx === void 0 ? void 0 : userMetadataEdx.profile_image) === null || _b === void 0 ? void 0 : _b.has_image) ? (jsx(AvatarImage, { src: (_c = userMetadataEdx === null || userMetadataEdx === void 0 ? void 0 : userMetadataEdx.profile_image) === null || _c === void 0 ? void 0 : _c.image_url_full, alt: t('profileIndex.altProfilePicture'), className: "rounded-full object-cover w-full h-full group-hover:opacity-80 transition-opacity" })) : customization.useGravatarPicFallback ? (jsx(Gravatar, { className: "w-full h-full rounded-full object-cover group-hover:opacity-80 transition-opacity", email: userMetadata === null || userMetadata === void 0 ? void 0 : userMetadata.email, size: 56, alt: t('profileIndex.altProfilePictureGravatar') })) : (jsx(Fragment$1, {})), jsx(AvatarFallback, { className: cn('bg-blue-800 text-white text-lg font-semibold rounded-full', customization.useGravatarPicFallback && !((_d = userMetadataEdx === null || userMetadataEdx === void 0 ? void 0 : userMetadataEdx.profile_image) === null || _d === void 0 ? void 0 : _d.image_url_full)
218981
220208
  ? 'hidden'
218982
220209
  : ''), children: getInitials((userMetadata === null || userMetadata === void 0 ? void 0 : userMetadata.name) || (userMetadata === null || userMetadata === void 0 ? void 0 : userMetadata.username) || (userMetadata === null || userMetadata === void 0 ? void 0 : userMetadata.email) || '') })] }));
218983
220210
  const handleFileUpload = (event) => {
@@ -219022,115 +220249,122 @@ function Profile({ tenant, username, tenants, onClose, customization = {}, isAdm
219022
220249
  // Rendered inline on the sub-tabs row (education/experience/resume), pushed to
219023
220250
  // the far right. `headerAdd` already encodes which sub-tab is active, so this
219024
220251
  // only appears on the sub-tabs that support adding/uploading.
219025
- const headerAddButton = headerAdd && (jsx(Button$1, { size: "icon", onClick: headerAdd.onClick, className: "text-gray-700 bg-gradient-to-r from-[#2563EB] to-[#93C5FD] hover:opacity-90 text-white flex-shrink-0", "aria-label": headerAdd.ariaLabel, children: headerAdd.upload ? jsx(Upload, { className: "h-4 w-4" }) : jsx(Plus, { className: "h-4 w-4" }) }));
220252
+ const headerAddButton = headerAdd && (jsxs(Button$1, { size: "sm", onClick: headerAdd.onClick, className: cn('h-9 flex-shrink-0 gap-1.5 px-3', PRIMARY_BUTTON_CLASS), "aria-label": headerAdd.ariaLabel, children: [headerAdd.upload ? (jsx(Upload, { className: "h-4 w-4", "aria-hidden": "true" })) : (jsx(Plus, { className: "h-4 w-4", "aria-hidden": "true" })), jsx("span", { className: "hidden sm:inline", children: headerAdd.ariaLabel })] }));
219026
220253
  const tabDescription = (id) => {
219027
- const key = (isReadOnly && TAB_DESC_PREVIEW_KEYS[id]) || TAB_DESC_KEYS[id];
220254
+ const key = (isReadOnly && TAB_DESC_PREVIEW_KEYS[id]) || TAB_DESC_KEYS$1[id];
219028
220255
  return key ? t(`profileIndex.${key}`) : '';
219029
220256
  };
219030
- return (jsxs("div", { className: "flex flex-col lg:flex-row h-full", children: [jsx("div", { className: "lg:hidden", children: jsx("div", { className: "px-6 py-4 border-b border-gray-200", children: jsx("h2", { className: "text-lg font-semibold text-gray-900", children: t('profileIndex.profileHeading') }) }) }), jsxs("div", { className: "hidden lg:flex bg-white border-r border-gray-200 dark:border-gray-700 flex-col overflow-hidden", style: {
219031
- width: '320px',
219032
- height: '100%',
219033
- }, children: [jsxs("div", { className: "flex flex-col items-center justify-center py-6 px-6 border-b border-gray-200 dark:border-gray-700 flex-shrink-0", children: [isUserMetadataEdxLoading || isUploadingProfileImage ? (jsx(Skeleton, { className: "h-20 w-20 mb-6 rounded-full", "aria-label": t('profileIndex.ariaLoadingProfileImage') })) : isReadOnly ? (
219034
- // Someone else's profile: show the picture, offer no way to replace it.
219035
- jsx("div", { className: "relative w-20 h-20 mb-4 rounded-full", children: profileAvatar })) : (jsxs("button", { type: "button", className: "relative w-20 h-20 mb-4 cursor-pointer group rounded-full focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2", onMouseEnter: () => setIsHovering(true), onMouseLeave: () => setIsHovering(false), onClick: handleAvatarClick, "aria-label": t('profileIndex.ariaUploadProfilePicture'), children: [profileAvatar, isHovering && (jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-blue-800 rounded-full border-4 border-white/20", "aria-hidden": "true", children: jsx("span", { className: "text-white text-xs font-medium text-center px-2", children: t('profileIndex.uploadOverlay') }) })), jsx("input", { ref: fileInputRef, type: "file", accept: "image/*", onChange: handleFileUpload, className: "hidden", "aria-label": t('profileIndex.ariaChooseProfilePicture'), tabIndex: -1 })] })), jsx("h2", { className: "text-base font-semibold text-gray-900 dark:text-gray-100 text-center mb-3 break-all px-2", title: userMetadata === null || userMetadata === void 0 ? void 0 : userMetadata.email, children: userMetadata === null || userMetadata === void 0 ? void 0 : userMetadata.email }), jsxs("div", { className: "flex justify-center space-x-3", children: [jsxs("div", { className: "bg-blue-100 text-blue-700 px-2 py-1 rounded font-medium flex items-center text-sm", children: [jsx(RoleIcon, { className: "h-4 w-4 mr-2", "aria-hidden": "true" }), jsx("span", { children: isAdmin ? t('profileIndex.roleAdmin') : t('profileIndex.roleUser') })] }), customization.showPlatformName && (jsx("div", { className: "bg-gray-100 text-gray-600 px-4 py-2 rounded text-xs font-medium", children: tenant.toUpperCase() }))] })] }), jsx("nav", { className: "flex-1 overflow-y-auto", style: { minHeight: 0 }, "aria-label": t('profileIndex.ariaProfileTabs'), children: jsx("div", { className: "p-4", children: jsx("div", { className: "flex flex-col space-y-1.5", role: "tablist", "aria-orientation": "vertical", children: TABS.map((tab) => (jsxs("button", { role: "tab", "aria-selected": activeTab === tab.id, "aria-controls": `${tab.id}-tabpanel`, id: `${tab.id}-tab`, onClick: () => setActiveTab(tab.id), className: `w-full justify-start px-4 py-2.5 text-left rounded-lg transition-all flex items-center text-base ${activeTab === tab.id
219036
- ? 'bg-blue-50 text-blue-700 font-medium'
219037
- : 'hover:bg-gray-50 text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100'}`, children: [jsx(tab.renderIcon, { className: "h-5 w-5 mr-3 flex-shrink-0", "aria-hidden": "true" }), jsx("span", { className: "truncate", children: tab.label })] }, tab.id))) }) }) })] }), jsx("nav", { className: "lg:hidden", "aria-label": t('profileIndex.ariaProfileTabsMobile'), children: jsx("div", { className: "w-full justify-start px-6 py-2 bg-white border-b border-gray-200 rounded-none h-auto overflow-x-auto", children: jsx("div", { className: "flex space-x-2", role: "tablist", "aria-orientation": "horizontal", children: TABS.map((tab) => (jsxs("button", { role: "tab", "aria-selected": activeTab === tab.id, "aria-controls": `${tab.id}-tabpanel`, id: `${tab.id}-tab-mobile`, onClick: () => setActiveTab(tab.id), className: `flex items-center gap-2 px-3 py-2 rounded-lg whitespace-nowrap text-sm transition-all ${activeTab === tab.id
219038
- ? 'bg-blue-50 text-blue-600 font-medium'
219039
- : 'text-gray-600 hover:bg-gray-50'}`, children: [jsx(tab.renderIcon, { className: "h-4 w-4", "aria-hidden": "true" }), jsx("span", { className: "hidden sm:inline", children: tab.label }), jsx("span", { className: "sm:hidden", children: tab.label })] }, tab.id))) }) }) }), jsxs("div", { className: "flex-1 flex flex-col overflow-hidden", style: { height: '100%' }, children: [jsx("div", { className: "hidden lg:block flex-shrink-0 p-6 border-b border-gray-200 bg-white dark:bg-gray-900", children: jsx("div", { className: "flex items-center justify-between", children: jsxs("div", { children: [jsx("h3", { className: "text-lg font-medium text-gray-900 dark:text-gray-100 mb-2", children: (_f = (_e = TABS.find((t) => t.id === activeTab)) === null || _e === void 0 ? void 0 : _e.label) !== null && _f !== void 0 ? _f : activeTab }), jsx("p", { className: "text-gray-600 dark:text-gray-400 text-sm", children: tabDescription(activeTab) })] }) }) }), jsxs("div", { className: "flex-1 p-6 space-y-6", style: {
219040
- overflowY: 'auto',
219041
- overflowX: 'hidden',
219042
- }, role: "tabpanel", id: `${activeTab}-tabpanel`, "aria-labelledby": `${activeTab}-tab`, tabIndex: 0, children: [jsx("div", { className: "lg:hidden mb-6", children: jsx("div", { className: "flex flex-row items-center sm:justify-between gap-3", children: jsxs("div", { children: [jsx("h3", { className: "text-lg font-medium text-gray-900 dark:text-gray-100 mb-2", children: (_h = (_g = TABS.find((t) => t.id === activeTab)) === null || _g === void 0 ? void 0 : _g.label) !== null && _h !== void 0 ? _h : activeTab }), jsx("p", { className: "text-gray-600 dark:text-gray-400 text-sm", children: tabDescription(activeTab) })] }) }) }), activeTab === 'basic' && (jsx("div", { className: "space-y-6", children: jsxs("form", { onSubmit: (formEvent) => {
219043
- formEvent.preventDefault();
219044
- formEvent.stopPropagation();
219045
- if (isReadOnly)
219046
- return;
219047
- basicForm.handleSubmit();
219048
- }, className: "space-y-6", children: [jsxs("div", { className: "space-y-2", children: [jsx("label", { htmlFor: "fullName", className: "text-gray-500 dark:text-gray-400 text-base", children: t('profileIndex.labelFullName') }), basicForm.Field({
219049
- name: 'fullName',
219050
- validators: {
219051
- onChange: basicValidators.fullName,
219052
- onSubmit: basicValidators.fullName,
219053
- },
219054
- children: (field) => {
219055
- var _a;
219056
- return (jsxs(Fragment$1, { children: [jsx(Input, { id: "fullName", className: "w-full bg-gray-50 border-gray-200 focus:ring-blue-500 focus:border-blue-500 text-base py-3", value: field.state.value, onChange: (e) => field.handleChange(e.target.value), disabled: basicForm.state.isSubmitting || isUserMetadataLoading || isReadOnly }), ((_a = field.state.meta.errors) === null || _a === void 0 ? void 0 : _a.length) > 0 && (jsx("p", { className: "text-red-500 text-sm mt-1", role: "alert", children: field.state.meta.errors }))] }));
219057
- },
219058
- })] }), jsxs("div", { className: "space-y-2", children: [jsx("label", { htmlFor: "email", className: "text-gray-500 dark:text-gray-400 text-base", children: t('profileIndex.labelEmail') }), basicForm.Field({
219059
- name: 'email',
219060
- children: (field) => (jsx(Input, { id: "email", type: "email", className: "w-full bg-gray-50 border-gray-200 focus:ring-blue-500 focus:border-blue-500 text-base py-3", value: field.state.value, onChange: (e) => field.handleChange(e.target.value), disabled: basicForm.state.isSubmitting || isUserMetadataLoading || isReadOnly })),
219061
- })] }), customization.showUsernameField && (jsxs("div", { className: "space-y-2", children: [jsx("label", { htmlFor: "username", className: "text-gray-500 dark:text-gray-400 text-base", children: t('profileIndex.labelUsername') }), basicForm.Field({
219062
- name: 'username',
219063
- children: (field) => (jsx(Input, { id: "username", className: "w-full bg-gray-50 border-gray-200 focus:ring-blue-500 focus:border-blue-500 text-base py-3", value: field.state.value, onChange: (e) => field.handleChange(e.target.value), disabled: basicForm.state.isSubmitting || isUserMetadataLoading || isReadOnly })),
219064
- })] })), jsxs("div", { className: "space-y-2", children: [jsx("label", { htmlFor: "title", className: "text-gray-500 dark:text-gray-400 text-base", children: t('profileIndex.labelTitle') }), basicForm.Field({
219065
- name: 'title',
219066
- children: (field) => (jsx(Input, { id: "title", className: "w-full bg-gray-50 border-gray-200 focus:ring-blue-500 focus:border-blue-500 text-base py-3", value: field.state.value, onChange: (e) => field.handleChange(e.target.value), disabled: basicForm.state.isSubmitting || isUserMetadataLoading || isReadOnly })),
219067
- })] }), jsxs("div", { className: "space-y-2", children: [jsx("label", { htmlFor: "about", className: "text-gray-500 dark:text-gray-400 text-base", children: t('profileIndex.labelAbout') }), basicForm.Field({
219068
- name: 'about',
219069
- children: (field) => (jsx(Textarea, { id: "about", placeholder: t('profileIndex.placeholderAbout'), className: "w-full bg-gray-50 border-gray-200 focus:ring-blue-500 focus:border-blue-500 text-base resize-none", rows: 4, value: field.state.value, onChange: (e) => field.handleChange(e.target.value), disabled: basicForm.state.isSubmitting || isUserMetadataLoading || isReadOnly })),
219070
- })] }), jsxs("div", { className: "space-y-2", children: [jsx("label", { htmlFor: "language", className: "text-gray-500 dark:text-gray-400 text-base", children: t('profileIndex.labelLanguage') }), basicForm.Field({
219071
- name: 'language',
219072
- children: (field) => (jsxs(Select$1, { defaultValue: field.state.value, onValueChange: (value) => field.handleChange(value), disabled: basicForm.state.isSubmitting || isUserMetadataLoading || isReadOnly, children: [jsx(SelectTrigger, { className: "w-full bg-gray-50 border-gray-200 focus:ring-blue-500 focus:border-blue-500 text-base py-3 text-sm", "aria-label": t('profileIndex.selectLanguagePlaceholder'), children: jsx(SelectValue, { placeholder: t('profileIndex.selectLanguagePlaceholder') }) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "en", children: t('profileIndex.langEnglish') }), jsx(SelectItem, { value: "es", children: t('profileIndex.langSpanish') }), jsx(SelectItem, { value: "fr", children: t('profileIndex.langFrench') })] })] })),
219073
- })] }), (customization.showLeaderboardDisplayCheckbox ||
219074
- customization.showMentorAIDisplayCheckbox) && (jsxs("div", { className: "grid grid-cols-2 gap-6 pt-4", children: [customization.showMentorAIDisplayCheckbox && (jsx("div", { className: "space-y-2", children: jsxs("div", { className: "flex items-center space-x-2", children: [basicForm.Field({
219075
- name: 'displayMentor',
219076
- children: (field) => (jsx(Switch, { className: "data-[state=checked]:bg-blue-600", onCheckedChange: (checked) => field.handleChange(checked), checked: field.state.value, disabled: basicForm.state.isSubmitting ||
219077
- isUserMetadataLoading ||
219078
- isReadOnly, id: "display-mentor" })),
219079
- }), jsx("label", { htmlFor: "display-mentor", className: "text-sm leading-none font-medium text-gray-700 peer-disabled:cursor-not-allowed peer-disabled:opacity-70", children: t('profileIndex.checkboxDisplayMentorAI') })] }) })), customization.showLeaderboardDisplayCheckbox && (jsx("div", { className: "space-y-2", children: jsxs("div", { className: "flex items-center space-x-2", children: [basicForm.Field({
219080
- name: 'displayLeaderBoard',
219081
- children: (field) => (jsx(Switch, { className: "data-[state=checked]:bg-blue-600", onCheckedChange: (checked) => field.handleChange(checked), checked: field.state.value, disabled: basicForm.state.isSubmitting ||
219082
- isUserMetadataLoading ||
219083
- isReadOnly, id: "display-leaderboard" })),
219084
- }), jsx("label", { htmlFor: "display-leaderboard", className: "text-sm leading-none font-medium text-gray-700 peer-disabled:cursor-not-allowed peer-disabled:opacity-70", children: t('profileIndex.checkboxDisplayLeaderboard') })] }) }))] }))] }) })), activeTab === 'social' && (jsx("div", { className: "space-y-6", children: jsxs("form", { className: "space-y-6", onSubmit: (formEvent) => {
219085
- formEvent.preventDefault();
219086
- formEvent.stopPropagation();
219087
- if (isReadOnly)
219088
- return;
219089
- socialForm.handleSubmit();
219090
- }, children: [jsxs("div", { className: "space-y-2", children: [jsx("label", { htmlFor: "facebook", className: "text-gray-500 dark:text-gray-400 text-base", children: t('profileIndex.labelFacebook') }), socialForm.Field({
219091
- name: 'facebook',
219092
- validators: {
219093
- onChange: socialValidators.facebook,
219094
- },
219095
- children: (field) => {
219096
- var _a;
219097
- return (jsxs(Fragment$1, { children: [jsxs("div", { className: "relative mb-0", children: [jsx("div", { className: "absolute inset-y-0 left-4 flex items-center pointer-events-none", "aria-hidden": "true", children: jsx("div", { className: "bg-[#1877F2] text-white rounded w-8 h-8 flex items-center justify-center", children: jsx("span", { className: "font-bold text-sm", children: "f" }) }) }), jsx(Input, { id: "facebook", className: "w-full bg-gray-50 border-gray-200 focus:ring-blue-500 focus:border-blue-500 text-base py-3 pl-16 h-10", placeholder: t('profileIndex.placeholderFacebook'), value: field.state.value, onChange: (e) => handleSocialLinkChange(e.target.value, field.handleChange, FACEBOOK_URL_PREFIX), disabled: socialForm.state.isSubmitting || isReadOnly })] }), jsx("p", { className: "text-red-500 dark:text-gray-400 text-sm", role: ((_a = field.state.meta.errors) === null || _a === void 0 ? void 0 : _a.length) ? 'alert' : undefined, "aria-live": "polite", children: field.state.meta.errors })] }));
219098
- },
219099
- })] }), jsxs("div", { className: "space-y-2", children: [jsx("label", { htmlFor: "linkedin", className: "text-gray-500 dark:text-gray-400 text-base", children: t('profileIndex.labelLinkedIn') }), socialForm.Field({
219100
- name: 'linkedIn',
219101
- validators: {
219102
- onChange: socialValidators.linkedIn,
219103
- },
219104
- children: (field) => {
219105
- var _a;
219106
- return (jsxs(Fragment$1, { children: [jsxs("div", { className: "relative mb-0", children: [jsx("div", { className: "absolute inset-y-0 left-4 flex items-center pointer-events-none", "aria-hidden": "true", children: jsx("div", { className: "bg-[#0A66C2] text-white rounded w-8 h-8 flex items-center justify-center", children: jsx("span", { className: "font-bold text-xs", children: "in" }) }) }), jsx(Input, { id: "linkedin", className: "w-full bg-gray-50 border-gray-200 focus:ring-blue-500 focus:border-blue-500 text-base py-3 pl-16 h-10", placeholder: t('profileIndex.placeholderLinkedIn'), value: field.state.value, onChange: (e) => handleSocialLinkChange(e.target.value, field.handleChange, LINKEDIN_URL_PREFIX), disabled: socialForm.state.isSubmitting || isReadOnly })] }), jsx("p", { className: "text-red-500 dark:text-gray-400 text-sm", role: ((_a = field.state.meta.errors) === null || _a === void 0 ? void 0 : _a.length) ? 'alert' : undefined, "aria-live": "polite", children: field.state.meta.errors })] }));
219107
- },
219108
- })] }), jsxs("div", { className: "space-y-2", children: [jsx("label", { htmlFor: "x", className: "text-gray-500 dark:text-gray-400 text-base", children: t('profileIndex.labelX') }), socialForm.Field({
219109
- name: 'x',
219110
- validators: {
219111
- onChange: socialValidators.x,
219112
- },
219113
- children: (field) => {
219114
- var _a;
219115
- return (jsxs(Fragment$1, { children: [jsxs("div", { className: "relative mb-0", children: [jsx("div", { className: "absolute inset-y-0 left-4 flex items-center pointer-events-none", "aria-hidden": "true", children: jsx("div", { className: "bg-black text-white rounded w-8 h-8 flex items-center justify-center", children: jsx("span", { className: "font-bold text-sm", children: "X" }) }) }), jsx(Input, { id: "x", className: "w-full bg-gray-50 border-gray-200 focus:ring-blue-500 focus:border-blue-500 text-base py-3 pl-16 h-10", placeholder: t('profileIndex.placeholderX'), value: field.state.value, onChange: (e) => handleSocialLinkChange(e.target.value, field.handleChange, X_URL_PREFIX), disabled: socialForm.state.isSubmitting || isReadOnly })] }), jsx("p", { className: "text-red-500 dark:text-gray-400 text-sm", role: ((_a = field.state.meta.errors) === null || _a === void 0 ? void 0 : _a.length) ? 'alert' : undefined, "aria-live": "polite", children: field.state.meta.errors })] }));
219116
- },
219117
- })] })] }) })), activeTab === 'education' && (jsxs(Tabs, { value: educationSubTab, onValueChange: setEducationSubTab, className: "space-y-4", children: [jsxs("div", { className: "flex items-center justify-between gap-3", children: [jsxs(TabsList, { children: [jsx(TabsTrigger, { value: "education", children: t('profileIndex.tabEducation') }), jsx(TabsTrigger, { value: "credentials", children: t('gradebookGradebookTab.typeCredentials') }), jsx(TabsTrigger, { value: "skills", children: t('gradebookGradebookTab.typeSkills') })] }), headerAddButton] }), jsx(TabsContent, { value: "education", children: jsx(EducationTab, { org: tenant, username: username }) }), jsx(TabsContent, { value: "credentials", children: jsx(CredentialsList, { org: tenant, username: username }) }), jsx(TabsContent, { value: "skills", children: jsx(SkillsList, { org: tenant, username: username }) })] })), activeTab === 'gradebook' && jsx(GradebookTab, { org: tenant, username: username }), activeTab === 'experience' && (jsxs(Tabs, { value: experienceSubTab, onValueChange: setExperienceSubTab, className: "space-y-4", children: [jsxs("div", { className: "flex items-center justify-between gap-3", children: [jsxs(TabsList, { children: [jsx(TabsTrigger, { value: "experience", children: t('profileIndex.tabExperience') }), jsx(TabsTrigger, { value: "resume", children: t('profileIndex.tabResume') })] }), headerAddButton] }), jsx(TabsContent, { value: "experience", children: jsx(ExperienceTab, { org: tenant, username: username }) }), jsx(TabsContent, { value: "resume", children: jsx(ResumeTab, { org: tenant, username: username }) })] })), activeTab === 'purchases' && jsx(PurchasesTab, { org: tenant, username: username }), activeTab === 'memory' && jsx(MemoryTab, { org: tenant, username: username }), activeTab === 'privacy' && jsx(ChatPrivacyTab, { org: tenant, username: username }), isChatHistoryVisible && activeTab === 'chatHistory' && (jsx(ChatHistoryTab, { org: tenant, username: username })), !isReadOnly && activeTab === 'security' && (jsx(Security, { app: currentSPA || '', email: (userMetadata === null || userMetadata === void 0 ? void 0 : userMetadata.email) || '', platformKey: tenant, onAccountDeleted: onAccountDeleted })), activeTab === 'advanced' && localLLMProps && (jsx("div", { className: "space-y-6", children: jsx(LocalModelsContent, { isAvailable: localLLMProps.isAvailable, state: localLLMProps.state, ollamaStatus: localLLMProps.ollamaStatus, systemMemory: localLLMProps.systemMemory, isUsingFoundry: localLLMProps.isUsingFoundry, foundryModels: localLLMProps.foundryModels, selectedFoundryModel: localLLMProps.selectedFoundryModel, foundryStatus: localLLMProps.foundryStatus, onStartDownload: localLLMProps.onStartDownload, onCancelDownload: localLLMProps.onCancelDownload, onInstallOllama: localLLMProps.onInstallOllama, onStopManager: localLLMProps.onStopManager, onInstallFoundry: localLLMProps.onInstallFoundry, onCheckStatus: localLLMProps.onCheckStatus, onResetState: localLLMProps.onResetState, onSelectFoundryModel: localLLMProps.onSelectFoundryModel }) }))] }), !isReadOnly && ['basic', 'social'].includes(activeTab) && (jsx("div", { className: "flex-shrink-0 border-t border-gray-200 p-6 bg-white dark:bg-gray-900", children: jsxs("div", { className: "flex justify-end gap-3", children: [jsx(Button$1, { onClick: onClose, variant: "outline", children: t('profileIndex.buttonCancel') }), jsx(Button$1, { onClick: () => {
219118
- if (activeTab === 'basic') {
219119
- basicForm.handleSubmit();
219120
- }
219121
- else if (activeTab === 'social') {
219122
- socialForm.handleSubmit();
219123
- }
219124
- }, className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] hover:opacity-90 text-white", disabled: (activeTab === 'basic' &&
219125
- (basicForm.state.isSubmitting || isUserMetadataLoading)) ||
219126
- (activeTab === 'social' && socialForm.state.isSubmitting), children: (activeTab === 'basic' && basicForm.state.isSubmitting) ||
219127
- (activeTab === 'social' && socialForm.state.isSubmitting)
219128
- ? t('profileIndex.buttonSaving')
219129
- : t('profileIndex.buttonSaveChanges') })] }) }))] }), jsx(EducationDialog, { open: isEducationDialogOpen, onOpenChange: setIsEducationDialogOpen, education: undefined, org: tenant, username: username, onComplete: () => {
219130
- // The education tab will handle refetching
219131
- } }), jsx(ExperienceDialog, { open: isExperienceDialogOpen, onOpenChange: setIsExperienceDialogOpen, experience: undefined, org: tenant, username: username, onComplete: () => {
219132
- // The experience tab will handle refetching
219133
- } }), jsx(ToastProvider, {})] }));
220257
+ const email = (userMetadata === null || userMetadata === void 0 ? void 0 : userMetadata.email) || '';
220258
+ const displayName = (userMetadata === null || userMetadata === void 0 ? void 0 : userMetadata.name) || (userMetadata === null || userMetadata === void 0 ? void 0 : userMetadata.username) || email;
220259
+ const roleChip = (jsx(SettingsChip, { tone: "accent", icon: User, children: isAdmin ? t('profileIndex.roleAdmin') : t('profileIndex.roleUser') }));
220260
+ const platformChip = customization.showPlatformName ? (jsx(SettingsChip, { children: tenant.toUpperCase() })) : null;
220261
+ const avatarBlock = isUserMetadataEdxLoading || isUploadingProfileImage ? (jsx(Skeleton, { className: "h-14 w-14 flex-shrink-0 rounded-full", "aria-label": t('profileIndex.ariaLoadingProfileImage') })) : isReadOnly ? (
220262
+ // Someone else's profile: show the picture, offer no way to replace it.
220263
+ jsx("div", { className: "relative h-14 w-14 flex-shrink-0 rounded-full", children: profileAvatar })) : (jsxs("button", { type: "button", className: "group relative h-14 w-14 flex-shrink-0 cursor-pointer rounded-full focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2", onMouseEnter: () => setIsHovering(true), onMouseLeave: () => setIsHovering(false), onClick: handleAvatarClick, "aria-label": t('profileIndex.ariaUploadProfilePicture'), children: [profileAvatar, isHovering && (jsx("div", { className: "absolute inset-0 flex items-center justify-center rounded-full bg-blue-900/80", "aria-hidden": "true", children: jsx("span", { className: "px-1 text-center text-[11px] font-semibold text-white", children: t('profileIndex.uploadOverlay') }) })), jsx("span", { className: "absolute -bottom-0.5 -right-0.5 flex h-6 w-6 items-center justify-center rounded-full border border-gray-200 bg-white text-gray-700 shadow-sm dark:border-gray-700 dark:bg-gray-800 dark:text-gray-200", "aria-hidden": "true", children: jsx(Camera, { className: "h-3 w-3" }) }), jsx("input", { ref: fileInputRef, type: "file", accept: "image/*", onChange: handleFileUpload, className: "hidden", "aria-label": t('profileIndex.ariaChooseProfilePicture'), tabIndex: -1 })] }));
220264
+ const sidebarHeader = (jsxs("div", { className: "flex flex-col gap-3", children: [jsxs("div", { className: "flex items-center gap-3", children: [avatarBlock, jsxs("div", { className: "min-w-0 space-y-1", children: [jsx("h2", { className: "truncate text-[15px] font-semibold text-gray-900 dark:text-gray-100", title: displayName, children: displayName }), jsxs("div", { className: "flex flex-wrap gap-1.5", children: [roleChip, platformChip] })] })] }), email && email !== displayName && (jsx("p", { className: "break-all text-[13px] leading-snug text-gray-500 dark:text-gray-400", title: email, children: email }))] }));
220265
+ // Name only: the email already sits in the sidebar card, which small
220266
+ // screens hide, and the dialog title covers the rest.
220267
+ const mobileHeader = (jsx("p", { className: "truncate text-[15px] font-semibold text-gray-900 dark:text-gray-100", children: (userMetadata === null || userMetadata === void 0 ? void 0 : userMetadata.name) || (userMetadata === null || userMetadata === void 0 ? void 0 : userMetadata.username) || t('profileIndex.profileHeading') }));
220268
+ const isBusyBasic = basicForm.state.isSubmitting || isUserMetadataLoading || isReadOnly;
220269
+ const footer = !isReadOnly && ['basic', 'social'].includes(activeTab) ? (jsxs("div", { className: "flex justify-end gap-3", children: [jsx(Button$1, { onClick: onClose, variant: "outline", className: "h-10 px-4", children: t('profileIndex.buttonCancel') }), jsx(Button$1, { onClick: () => {
220270
+ if (activeTab === 'basic') {
220271
+ basicForm.handleSubmit();
220272
+ }
220273
+ else if (activeTab === 'social') {
220274
+ socialForm.handleSubmit();
220275
+ }
220276
+ }, className: cn('h-10 px-5', PRIMARY_BUTTON_CLASS), disabled: (activeTab === 'basic' && (basicForm.state.isSubmitting || isUserMetadataLoading)) ||
220277
+ (activeTab === 'social' && socialForm.state.isSubmitting), children: (activeTab === 'basic' && basicForm.state.isSubmitting) ||
220278
+ (activeTab === 'social' && socialForm.state.isSubmitting)
220279
+ ? t('profileIndex.buttonSaving')
220280
+ : t('profileIndex.buttonSaveChanges') })] })) : null;
220281
+ const socialField = ({ id, name, label, placeholder, prefix, badge, validator, }) => (jsxs("div", { className: "space-y-1.5", children: [jsx("label", { htmlFor: id, className: LABEL_CLASS, children: label }), socialForm.Field({
220282
+ name,
220283
+ validators: {
220284
+ onChange: validator,
220285
+ },
220286
+ children: (field) => {
220287
+ var _a;
220288
+ return (jsxs(Fragment$1, { children: [jsxs("div", { className: "relative mb-0", children: [jsx("div", { className: "pointer-events-none absolute inset-y-0 left-2 flex items-center", "aria-hidden": "true", children: jsx("div", { className: cn('flex h-7 w-7 items-center justify-center rounded-md text-white', badge.className), children: jsx("span", { className: cn('font-bold', badge.textClassName), children: badge.text }) }) }), jsx(Input, { id: id, className: cn(FIELD_CLASS, 'pl-12'), placeholder: placeholder, value: field.state.value, onChange: (e) => handleSocialLinkChange(e.target.value, field.handleChange, prefix), disabled: socialForm.state.isSubmitting || isReadOnly })] }), jsx("p", { className: "text-sm text-red-600 dark:text-red-400", role: ((_a = field.state.meta.errors) === null || _a === void 0 ? void 0 : _a.length) ? 'alert' : undefined, "aria-live": "polite", children: field.state.meta.errors })] }));
220289
+ },
220290
+ })] }));
220291
+ return (jsxs(SettingsShell, { sidebarHeader: sidebarHeader, sidebarNav: jsx(SettingsSidebarNav, { groups: navGroups, activeId: activeTab, onSelect: setActiveTab, variant: "tabs", ariaLabel: t('profileIndex.ariaProfileTabs') }), mobileHeader: mobileHeader, mobileNav: jsx(SettingsMobileNav, { groups: navGroups, activeId: activeTab, onSelect: setActiveTab, variant: "tabs", ariaLabel: t('profileIndex.ariaProfileTabsMobile') }),
220292
+ // Render the tab's label rather than the id — every label is already
220293
+ // cased correctly.
220294
+ title: (_f = (_e = TABS.find((tab) => tab.id === activeTab)) === null || _e === void 0 ? void 0 : _e.label) !== null && _f !== void 0 ? _f : activeTab, description: tabDescription(activeTab), panelProps: {
220295
+ role: 'tabpanel',
220296
+ id: `${activeTab}-tabpanel`,
220297
+ 'aria-labelledby': `${activeTab}-tab`,
220298
+ tabIndex: 0,
220299
+ }, footer: footer, overlays: jsxs(Fragment$1, { children: [jsx(EducationDialog, { open: isEducationDialogOpen, onOpenChange: setIsEducationDialogOpen, education: undefined, org: tenant, username: username, onComplete: () => {
220300
+ // The education tab will handle refetching
220301
+ } }), jsx(ExperienceDialog, { open: isExperienceDialogOpen, onOpenChange: setIsExperienceDialogOpen, experience: undefined, org: tenant, username: username, onComplete: () => {
220302
+ // The experience tab will handle refetching
220303
+ } }), jsx(ToastProvider, {})] }), children: [activeTab === 'basic' && (jsxs("form", { onSubmit: (formEvent) => {
220304
+ formEvent.preventDefault();
220305
+ formEvent.stopPropagation();
220306
+ if (isReadOnly)
220307
+ return;
220308
+ basicForm.handleSubmit();
220309
+ }, className: "max-w-3xl space-y-8", children: [jsxs("div", { className: "grid grid-cols-1 gap-x-5 gap-y-5 md:grid-cols-2", children: [jsxs("div", { className: "space-y-1.5", children: [jsx("label", { htmlFor: "fullName", className: LABEL_CLASS, children: t('profileIndex.labelFullName') }), basicForm.Field({
220310
+ name: 'fullName',
220311
+ validators: {
220312
+ onChange: basicValidators.fullName,
220313
+ onSubmit: basicValidators.fullName,
220314
+ },
220315
+ children: (field) => {
220316
+ var _a;
220317
+ return (jsxs(Fragment$1, { children: [jsx(Input, { id: "fullName", className: FIELD_CLASS, value: field.state.value, onChange: (e) => field.handleChange(e.target.value), disabled: isBusyBasic }), ((_a = field.state.meta.errors) === null || _a === void 0 ? void 0 : _a.length) > 0 && (jsx("p", { className: "mt-1 text-sm text-red-600 dark:text-red-400", role: "alert", children: field.state.meta.errors }))] }));
220318
+ },
220319
+ })] }), jsxs("div", { className: "space-y-1.5", children: [jsx("label", { htmlFor: "email", className: LABEL_CLASS, children: t('profileIndex.labelEmail') }), basicForm.Field({
220320
+ name: 'email',
220321
+ children: (field) => (jsx(Input, { id: "email", type: "email", className: FIELD_CLASS, value: field.state.value, onChange: (e) => field.handleChange(e.target.value), disabled: isBusyBasic })),
220322
+ })] }), customization.showUsernameField && (jsxs("div", { className: "space-y-1.5", children: [jsx("label", { htmlFor: "username", className: LABEL_CLASS, children: t('profileIndex.labelUsername') }), basicForm.Field({
220323
+ name: 'username',
220324
+ children: (field) => (jsx(Input, { id: "username", className: FIELD_CLASS, value: field.state.value, onChange: (e) => field.handleChange(e.target.value), disabled: isBusyBasic })),
220325
+ })] })), jsxs("div", { className: "space-y-1.5", children: [jsx("label", { htmlFor: "title", className: LABEL_CLASS, children: t('profileIndex.labelTitle') }), basicForm.Field({
220326
+ name: 'title',
220327
+ children: (field) => (jsx(Input, { id: "title", className: FIELD_CLASS, value: field.state.value, onChange: (e) => field.handleChange(e.target.value), disabled: isBusyBasic })),
220328
+ })] }), jsxs("div", { className: "space-y-1.5", children: [jsx("label", { htmlFor: "language", className: LABEL_CLASS, children: t('profileIndex.labelLanguage') }), basicForm.Field({
220329
+ name: 'language',
220330
+ children: (field) => (jsxs(Select$1, { defaultValue: field.state.value, onValueChange: (value) => field.handleChange(value), disabled: isBusyBasic, children: [jsx(SelectTrigger, { id: "language", className: FIELD_CLASS, "aria-label": t('profileIndex.selectLanguagePlaceholder'), children: jsx(SelectValue, { placeholder: t('profileIndex.selectLanguagePlaceholder') }) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "en", children: t('profileIndex.langEnglish') }), jsx(SelectItem, { value: "es", children: t('profileIndex.langSpanish') }), jsx(SelectItem, { value: "fr", children: t('profileIndex.langFrench') })] })] })),
220331
+ })] }), jsxs("div", { className: "space-y-1.5 md:col-span-2", children: [jsx("label", { htmlFor: "about", className: LABEL_CLASS, children: t('profileIndex.labelAbout') }), basicForm.Field({
220332
+ name: 'about',
220333
+ children: (field) => (jsx(Textarea, { id: "about", placeholder: t('profileIndex.placeholderAbout'), className: cn(FIELD_CLASS, 'h-auto resize-none py-2.5 leading-relaxed'), rows: 4, value: field.state.value, onChange: (e) => field.handleChange(e.target.value), disabled: isBusyBasic })),
220334
+ })] })] }), customization.showMentorAIDisplayCheckbox && (jsxs("section", { className: "space-y-2.5", "aria-labelledby": "profile-features-heading", children: [jsx("h4", { id: "profile-features-heading", className: "text-sm font-semibold text-gray-900 dark:text-gray-100", children: t('profileIndex.sectionFeatures') }), jsx("div", { className: "divide-y divide-gray-200 rounded-xl border border-gray-200 dark:divide-gray-800 dark:border-gray-800", children: jsxs("div", { className: "flex items-center justify-between gap-4 px-4 py-3.5", children: [jsxs("div", { className: "flex min-w-0 items-center gap-1", children: [jsx("label", { htmlFor: "display-mentor", className: "text-sm font-medium text-gray-700 peer-disabled:cursor-not-allowed peer-disabled:opacity-70 dark:text-gray-300", children: t('profileIndex.checkboxDisplayMentorAI') }), jsx(CardInfoIcon, { description: t('profileIndex.tooltipDisplayMentorAI') })] }), basicForm.Field({
220335
+ name: 'displayMentor',
220336
+ children: (field) => (jsx(Switch, { className: "data-[state=checked]:bg-blue-600", onCheckedChange: (checked) => field.handleChange(checked), checked: field.state.value, disabled: isBusyBasic, id: "display-mentor" })),
220337
+ })] }) })] }))] })), activeTab === 'social' && (jsxs("form", { className: "max-w-2xl space-y-5", onSubmit: (formEvent) => {
220338
+ formEvent.preventDefault();
220339
+ formEvent.stopPropagation();
220340
+ if (isReadOnly)
220341
+ return;
220342
+ socialForm.handleSubmit();
220343
+ }, children: [socialField({
220344
+ id: 'facebook',
220345
+ name: 'facebook',
220346
+ label: t('profileIndex.labelFacebook'),
220347
+ placeholder: t('profileIndex.placeholderFacebook'),
220348
+ prefix: FACEBOOK_URL_PREFIX,
220349
+ badge: { className: 'bg-[#1877F2]', text: 'f', textClassName: 'text-sm' },
220350
+ validator: socialValidators.facebook,
220351
+ }), socialField({
220352
+ id: 'linkedin',
220353
+ name: 'linkedIn',
220354
+ label: t('profileIndex.labelLinkedIn'),
220355
+ placeholder: t('profileIndex.placeholderLinkedIn'),
220356
+ prefix: LINKEDIN_URL_PREFIX,
220357
+ badge: { className: 'bg-[#0A66C2]', text: 'in', textClassName: 'text-xs' },
220358
+ validator: socialValidators.linkedIn,
220359
+ }), socialField({
220360
+ id: 'x',
220361
+ name: 'x',
220362
+ label: t('profileIndex.labelX'),
220363
+ placeholder: t('profileIndex.placeholderX'),
220364
+ prefix: X_URL_PREFIX,
220365
+ badge: { className: 'bg-black', text: 'X', textClassName: 'text-sm' },
220366
+ validator: socialValidators.x,
220367
+ })] })), activeTab === 'education' && (jsxs(Tabs, { value: educationSubTab, onValueChange: setEducationSubTab, className: "space-y-4", children: [jsxs("div", { className: "flex items-center justify-between gap-3", children: [jsxs(TabsList, { children: [jsx(TabsTrigger, { value: "education", children: t('profileIndex.tabEducation') }), jsx(TabsTrigger, { value: "credentials", children: t('gradebookGradebookTab.typeCredentials') }), jsx(TabsTrigger, { value: "skills", children: t('gradebookGradebookTab.typeSkills') })] }), headerAddButton] }), jsx(TabsContent, { value: "education", children: jsx(EducationTab, { org: tenant, username: username }) }), jsx(TabsContent, { value: "credentials", children: jsx(CredentialsList, { org: tenant, username: username }) }), jsx(TabsContent, { value: "skills", children: jsx(SkillsList, { org: tenant, username: username }) })] })), activeTab === 'gradebook' && jsx(GradebookTab, { org: tenant, username: username }), activeTab === 'experience' && (jsxs(Tabs, { value: experienceSubTab, onValueChange: setExperienceSubTab, className: "space-y-4", children: [jsxs("div", { className: "flex items-center justify-between gap-3", children: [jsxs(TabsList, { children: [jsx(TabsTrigger, { value: "experience", children: t('profileIndex.tabExperience') }), jsx(TabsTrigger, { value: "resume", children: t('profileIndex.tabResume') })] }), headerAddButton] }), jsx(TabsContent, { value: "experience", children: jsx(ExperienceTab, { org: tenant, username: username }) }), jsx(TabsContent, { value: "resume", children: jsx(ResumeTab, { org: tenant, username: username }) })] })), activeTab === 'purchases' && jsx(PurchasesTab, { org: tenant, username: username }), activeTab === 'memory' && jsx(MemoryTab, { org: tenant, username: username }), activeTab === 'privacy' && jsx(ChatPrivacyTab, { org: tenant, username: username }), isChatHistoryVisible && activeTab === 'chatHistory' && (jsx(ChatHistoryTab, { org: tenant, username: username })), isLlmUsageVisible && activeTab === 'llmUsage' && (jsx(LlmUsageTab, { org: tenant, username: username, isOwnProfile: !isReadOnly })), !isReadOnly && activeTab === 'security' && (jsx(Security, { app: currentSPA || '', email: (userMetadata === null || userMetadata === void 0 ? void 0 : userMetadata.email) || '', platformKey: tenant, onAccountDeleted: onAccountDeleted })), activeTab === 'advanced' && localLLMProps && (jsx("div", { className: "space-y-6", children: jsx(LocalModelsContent, { isAvailable: localLLMProps.isAvailable, state: localLLMProps.state, ollamaStatus: localLLMProps.ollamaStatus, systemMemory: localLLMProps.systemMemory, isUsingFoundry: localLLMProps.isUsingFoundry, foundryModels: localLLMProps.foundryModels, selectedFoundryModel: localLLMProps.selectedFoundryModel, foundryStatus: localLLMProps.foundryStatus, onStartDownload: localLLMProps.onStartDownload, onCancelDownload: localLLMProps.onCancelDownload, onInstallOllama: localLLMProps.onInstallOllama, onStopManager: localLLMProps.onStopManager, onInstallFoundry: localLLMProps.onInstallFoundry, onCheckStatus: localLLMProps.onCheckStatus, onResetState: localLLMProps.onResetState, onSelectFoundryModel: localLLMProps.onSelectFoundryModel }) }))] }));
219134
220368
  }
219135
220369
 
219136
220370
  function CSVEditor({ csvData, onSave, onCancel }) {
@@ -220517,6 +221751,624 @@ function InvitedUsersDialog({ tenant, onClose }) {
220517
221751
  }, children: jsx(DialogPortal, { forceMount: true, children: jsxs(DialogContent, { "aria-label": t('profileInvitedUsers.dialogAriaLabel'), className: "max-w-sm rounded-2xl px-6 py-8 pointer-events-auto", "aria-describedby": "invited-users-dialog", children: [jsx(DialogHeader, { className: "relative flex-row justify-between", children: jsx(DialogTitle, { className: "text-blue-600 text-xl font-semibold", children: t('profileInvitedUsers.title') }) }), jsxs("div", { className: "mt-4 space-y-1", children: [jsx("p", { className: "text-gray-500 text-sm", children: t('profileInvitedUsers.pendingDescription') }), jsx("form", { children: jsx(Input, { value: query, onChange: (event) => setQuery(event.target.value), placeholder: t('profileInvitedUsers.searchPlaceholder'), className: "mt-2" }) })] }), jsxs("div", { children: [jsx("h2", { className: "text-md text-gray-600 font-semibold mb-4", children: t('profileInvitedUsers.emailHeading') }), invitedUsers === null || invitedUsers === void 0 ? void 0 : invitedUsers.results.map((user) => (jsx("div", { className: "border-b-gray-50 pb-2 mb-4 text-sm text-gray-600 dark:text-gray-400", children: user.email }, user.id))), (invitedUsers === null || invitedUsers === void 0 ? void 0 : invitedUsers.results.length) === 0 && (jsx("p", { className: "text-gray", children: t('profileInvitedUsers.noRecords') }))] })] }) }) }));
220518
221752
  }
220519
221753
 
221754
+ function DocumentSidebar({ isModal = false, sessionId = '', documents, }) {
221755
+ const t = useT();
221756
+ const [isCollapsed, setIsCollapsed] = React.useState(false);
221757
+ const params = useParams();
221758
+ const username = useUsername();
221759
+ const isStreaming = useSelector(selectStreaming);
221760
+ const hasOwnDocuments = documents !== undefined;
221761
+ const { data: sessionDocuments, refetch, status, } = useGetVectorDocumentsQuery({
221762
+ org: params.tenantKey,
221763
+ sessionId: sessionId,
221764
+ // @ts-ignore
221765
+ userId: username,
221766
+ }, {
221767
+ skip: hasOwnDocuments || !params.tenantKey || !sessionId || !username || !isLoggedIn(),
221768
+ });
221769
+ const vectorDocuments = hasOwnDocuments
221770
+ ? (documents !== null && documents !== void 0 ? documents : undefined)
221771
+ : sessionDocuments;
221772
+ React.useEffect(() => {
221773
+ if (!hasOwnDocuments && !isStreaming && isLoggedIn() && status !== 'uninitialized') {
221774
+ refetch();
221775
+ }
221776
+ }, [hasOwnDocuments, isStreaming, refetch]);
221777
+ const toggleCollapse = () => {
221778
+ setIsCollapsed(!isCollapsed);
221779
+ };
221780
+ return (jsx(Fragment$1, { children: (vectorDocuments === null || vectorDocuments === void 0 ? void 0 : vectorDocuments.length) && vectorDocuments.length > 0 ? (jsx("aside", { className: cn('transition-all duration-300 ease-in-out', isModal
221781
+ ? 'h-full max-h-full w-full overflow-auto'
221782
+ : 'hidden rounded-tl-lg rounded-bl-lg md:hidden lg:block', !isModal && (isCollapsed ? 'w-[40px]' : 'w-[380px]')), style: {
221783
+ backgroundColor: isCollapsed && !isModal ? 'white' : '#F3F6FB',
221784
+ // In the dialog the parent bounds the height; the rail caps itself.
221785
+ maxHeight: isModal ? undefined : 'calc(100vh - 80px)',
221786
+ }, children: jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [!isModal && (jsxs("div", { className: cn('flex cursor-pointer items-center gap-2 border-b border-gray-200 p-4 transition-colors duration-200', isCollapsed && 'h-[80px] justify-center rounded-tl-[5px] rounded-bl-[5px] p-2', 'hover:bg-blue-100'), onClick: toggleCollapse, style: { backgroundColor: '#F3F6FB' }, children: [jsx(FileText, { className: "h-5 w-5 shrink-0 text-blue-600" }), !isCollapsed && (jsxs(Fragment$1, { children: [jsx("h2", { className: "text-base font-medium text-[#646464]", children: t('documentSidebar.retrievedDocuments') }), jsx(ChevronRight, { className: cn('ml-auto h-4 w-4 text-gray-400 transition-transform', isCollapsed ? 'rotate-180' : '') })] }))] })), (!isCollapsed || isModal) && (jsx(Fragment$1, { children: jsx("div", { className: "min-h-0 flex-1 overflow-auto", style: { backgroundColor: '#F3F6FB' }, children: jsx("div", { className: "mt-0 space-y-1 p-2", children: vectorDocuments.map((doc, index) => {
221787
+ var _a, _b;
221788
+ return (jsxs(React.Fragment, { children: [jsx("div", { className: "rounded-lg p-2 hover:bg-gray-50", children: jsxs("div", { className: "flex items-start gap-2", children: [jsx(Radio$1, { className: "mt-1 h-4 w-4 text-gray-400" }), jsxs("div", { className: "min-w-0 flex-1 space-y-1", children: [jsxs("div", { className: "flex items-center justify-between", children: [jsx(Link$2, { href: (_a = doc.source) !== null && _a !== void 0 ? _a : '', target: "_blank", className: "min-w-0", children: jsx("span", { className: "text-sm font-semibold break-words text-[#646464] hover:text-blue-600", children: doc.title }) }), jsx("div", { className: "flex items-center gap-2 self-center", children: jsx("span", { className: "text-xs text-gray-500", children: (_b = doc === null || doc === void 0 ? void 0 : doc.score) === null || _b === void 0 ? void 0 : _b.toFixed(2) }) })] }), jsx("p", { className: "line-clamp-6 pt-[10px] text-[14px] text-gray-600", children: doc.snippet })] })] }) }), index < vectorDocuments.length - 1 && (jsx("div", { className: "my-2 border-t border-gray-200" }))] }, index));
221789
+ }) }) }) }))] }) })) : null }));
221790
+ }
221791
+
221792
+ /**
221793
+ * The chat's "Retrieved Documents" control: an outline button opening the
221794
+ * documents dialog (`DocumentSidebar` in modal mode). Fed by the session's
221795
+ * vector results by default, or by `documents` the host already has — the
221796
+ * admin transcript surfaces pass a turn's or a conversation's documents, so
221797
+ * one component shows retrieved documents everywhere.
221798
+ */
221799
+ function RetrievedDocumentsButton({ sessionId = '', documents, className, label, }) {
221800
+ const t = useT();
221801
+ const [isOpen, setIsOpen] = useState(false);
221802
+ const params = useParams();
221803
+ const username = useUsername();
221804
+ const isStreaming = useSelector(selectStreaming);
221805
+ const hasOwnDocuments = documents !== undefined;
221806
+ const { data: sessionDocuments, refetch, status, } = useGetVectorDocumentsQuery({
221807
+ org: params.tenantKey,
221808
+ sessionId: sessionId,
221809
+ // @ts-ignore
221810
+ userId: username,
221811
+ }, {
221812
+ skip: hasOwnDocuments || !params.tenantKey || !sessionId || !username || !isLoggedIn(),
221813
+ });
221814
+ const vectorDocuments = hasOwnDocuments
221815
+ ? (documents !== null && documents !== void 0 ? documents : undefined)
221816
+ : sessionDocuments;
221817
+ useEffect(() => {
221818
+ if (!hasOwnDocuments && !isStreaming && isLoggedIn() && status !== 'uninitialized') {
221819
+ refetch();
221820
+ }
221821
+ }, [hasOwnDocuments, isStreaming, refetch]);
221822
+ if (!(vectorDocuments === null || vectorDocuments === void 0 ? void 0 : vectorDocuments.length)) {
221823
+ return null;
221824
+ }
221825
+ return (jsxs(Fragment$1, { children: [jsxs(Button$1, { variant: "outline", size: "sm", className: cn('flex items-center gap-2 border-gray-200 bg-white text-gray-700 hover:bg-gray-50', className), onClick: (event) => {
221826
+ // Safe inside clickable transcript rows: the row must not react.
221827
+ event.stopPropagation();
221828
+ setIsOpen(true);
221829
+ }, "data-testid": "retrieved-documents-button", children: [jsx(FileText, { className: "h-4 w-4 text-blue-600" }), jsx("span", { children: label !== null && label !== void 0 ? label : t('retrievedDocumentsButton.title') })] }), jsx(Dialog, { open: isOpen, onOpenChange: setIsOpen, children: jsxs(DialogContent, { className: "flex max-h-[80vh] flex-col gap-0 p-0 sm:max-w-[425px] md:max-w-[600px]", children: [jsx(DialogHeader, { className: "border-b px-4 py-2", children: jsxs(DialogTitle, { className: "flex items-center gap-2 text-base font-medium text-[#646464]", children: [jsx(FileText, { className: "h-5 w-5 text-blue-600" }), t('retrievedDocumentsButton.title')] }) }), jsx("div", { className: "min-h-0 flex-1 overflow-y-auto", "data-testid": "retrieved-documents-scroll", children: jsx(DocumentSidebar, { isModal: true, sessionId: sessionId, documents: documents }) })] }) })] }));
221830
+ }
221831
+
221832
+ var COLLAPSIBLE_NAME = "Collapsible";
221833
+ var [createCollapsibleContext] = createContextScope$1(COLLAPSIBLE_NAME);
221834
+ var [CollapsibleProvider, useCollapsibleContext] = createCollapsibleContext(COLLAPSIBLE_NAME);
221835
+ var Collapsible$1 = React.forwardRef(
221836
+ (props, forwardedRef) => {
221837
+ const {
221838
+ __scopeCollapsible,
221839
+ open: openProp,
221840
+ defaultOpen,
221841
+ disabled,
221842
+ onOpenChange,
221843
+ ...collapsibleProps
221844
+ } = props;
221845
+ const [open, setOpen] = useControllableState$1({
221846
+ prop: openProp,
221847
+ defaultProp: defaultOpen ?? false,
221848
+ onChange: onOpenChange,
221849
+ caller: COLLAPSIBLE_NAME
221850
+ });
221851
+ return /* @__PURE__ */ jsx(
221852
+ CollapsibleProvider,
221853
+ {
221854
+ scope: __scopeCollapsible,
221855
+ disabled,
221856
+ contentId: useId$1(),
221857
+ open,
221858
+ onOpenToggle: React.useCallback(() => setOpen((prevOpen) => !prevOpen), [setOpen]),
221859
+ children: /* @__PURE__ */ jsx(
221860
+ Primitive$2.div,
221861
+ {
221862
+ "data-state": getState$1(open),
221863
+ "data-disabled": disabled ? "" : void 0,
221864
+ ...collapsibleProps,
221865
+ ref: forwardedRef
221866
+ }
221867
+ )
221868
+ }
221869
+ );
221870
+ }
221871
+ );
221872
+ Collapsible$1.displayName = COLLAPSIBLE_NAME;
221873
+ var TRIGGER_NAME$2 = "CollapsibleTrigger";
221874
+ var CollapsibleTrigger$1 = React.forwardRef(
221875
+ (props, forwardedRef) => {
221876
+ const { __scopeCollapsible, ...triggerProps } = props;
221877
+ const context = useCollapsibleContext(TRIGGER_NAME$2, __scopeCollapsible);
221878
+ return /* @__PURE__ */ jsx(
221879
+ Primitive$2.button,
221880
+ {
221881
+ type: "button",
221882
+ "aria-controls": context.contentId,
221883
+ "aria-expanded": context.open || false,
221884
+ "data-state": getState$1(context.open),
221885
+ "data-disabled": context.disabled ? "" : void 0,
221886
+ disabled: context.disabled,
221887
+ ...triggerProps,
221888
+ ref: forwardedRef,
221889
+ onClick: composeEventHandlers$1(props.onClick, context.onOpenToggle)
221890
+ }
221891
+ );
221892
+ }
221893
+ );
221894
+ CollapsibleTrigger$1.displayName = TRIGGER_NAME$2;
221895
+ var CONTENT_NAME$3 = "CollapsibleContent";
221896
+ var CollapsibleContent$1 = React.forwardRef(
221897
+ (props, forwardedRef) => {
221898
+ const { forceMount, ...contentProps } = props;
221899
+ const context = useCollapsibleContext(CONTENT_NAME$3, props.__scopeCollapsible);
221900
+ return /* @__PURE__ */ jsx(Presence$2, { present: forceMount || context.open, children: ({ present }) => /* @__PURE__ */ jsx(CollapsibleContentImpl, { ...contentProps, ref: forwardedRef, present }) });
221901
+ }
221902
+ );
221903
+ CollapsibleContent$1.displayName = CONTENT_NAME$3;
221904
+ var CollapsibleContentImpl = React.forwardRef((props, forwardedRef) => {
221905
+ const { __scopeCollapsible, present, children, ...contentProps } = props;
221906
+ const context = useCollapsibleContext(CONTENT_NAME$3, __scopeCollapsible);
221907
+ const [isPresent, setIsPresent] = React.useState(present);
221908
+ const ref = React.useRef(null);
221909
+ const composedRefs = useComposedRefs$1(forwardedRef, ref);
221910
+ const heightRef = React.useRef(0);
221911
+ const height = heightRef.current;
221912
+ const widthRef = React.useRef(0);
221913
+ const width = widthRef.current;
221914
+ const isOpen = context.open || isPresent;
221915
+ const isMountAnimationPreventedRef = React.useRef(isOpen);
221916
+ const originalStylesRef = React.useRef(void 0);
221917
+ React.useEffect(() => {
221918
+ const rAF = requestAnimationFrame(() => isMountAnimationPreventedRef.current = false);
221919
+ return () => cancelAnimationFrame(rAF);
221920
+ }, []);
221921
+ useLayoutEffect2$1(() => {
221922
+ const node = ref.current;
221923
+ if (node) {
221924
+ originalStylesRef.current = originalStylesRef.current || {
221925
+ transitionDuration: node.style.transitionDuration,
221926
+ animationName: node.style.animationName
221927
+ };
221928
+ node.style.transitionDuration = "0s";
221929
+ node.style.animationName = "none";
221930
+ const rect = node.getBoundingClientRect();
221931
+ heightRef.current = rect.height;
221932
+ widthRef.current = rect.width;
221933
+ if (!isMountAnimationPreventedRef.current) {
221934
+ node.style.transitionDuration = originalStylesRef.current.transitionDuration;
221935
+ node.style.animationName = originalStylesRef.current.animationName;
221936
+ }
221937
+ setIsPresent(present);
221938
+ }
221939
+ }, [context.open, present]);
221940
+ return /* @__PURE__ */ jsx(
221941
+ Primitive$2.div,
221942
+ {
221943
+ "data-state": getState$1(context.open),
221944
+ "data-disabled": context.disabled ? "" : void 0,
221945
+ id: context.contentId,
221946
+ hidden: !isOpen,
221947
+ ...contentProps,
221948
+ ref: composedRefs,
221949
+ style: {
221950
+ [`--radix-collapsible-content-height`]: height ? `${height}px` : void 0,
221951
+ [`--radix-collapsible-content-width`]: width ? `${width}px` : void 0,
221952
+ ...props.style
221953
+ },
221954
+ children: isOpen && children
221955
+ }
221956
+ );
221957
+ });
221958
+ function getState$1(open) {
221959
+ return open ? "open" : "closed";
221960
+ }
221961
+ var Root$5 = Collapsible$1;
221962
+
221963
+ const Collapsible = Root$5;
221964
+ const CollapsibleTrigger = CollapsibleTrigger$1;
221965
+ const CollapsibleContent = CollapsibleContent$1;
221966
+
221967
+ function getFriendlyToolName(toolName) {
221968
+ if (TOOL_NAME_MAP === null || TOOL_NAME_MAP === void 0 ? void 0 : TOOL_NAME_MAP[toolName])
221969
+ return TOOL_NAME_MAP[toolName];
221970
+ const words = toolName.replace(/_/g, ' ').trim();
221971
+ return words.charAt(0).toUpperCase() + words.slice(1);
221972
+ }
221973
+ // Extract a short human-readable query from tool input or log
221974
+ function getQueryLabel(toolCall) {
221975
+ var _a, _b, _c, _d, _e;
221976
+ // Prefer structured input
221977
+ if (toolCall.input && typeof toolCall.input === 'object') {
221978
+ const val = (_c = (_b = (_a = toolCall.input.query) !== null && _a !== void 0 ? _a : toolCall.input.q) !== null && _b !== void 0 ? _b : toolCall.input.input) !== null && _c !== void 0 ? _c : toolCall.input.text;
221979
+ if (val && typeof val === 'string')
221980
+ return val;
221981
+ // Fallback: first string value in input
221982
+ const firstStr = Object.values(toolCall.input).find((v) => typeof v === 'string');
221983
+ if (firstStr)
221984
+ return firstStr;
221985
+ }
221986
+ // Fallback: parse the "… with `{...}`" snippet of the log string. Located
221987
+ // with index scans rather than a lazy regex, which backtracks
221988
+ // polynomially on adversarial logs (CodeQL js/polynomial-redos).
221989
+ if (toolCall.log) {
221990
+ const snippet = extractLogPayload(toolCall.log);
221991
+ if (snippet) {
221992
+ try {
221993
+ const parsed = JSON.parse(snippet.replace(/'/g, '"'));
221994
+ const val = (_e = (_d = parsed.query) !== null && _d !== void 0 ? _d : parsed.q) !== null && _e !== void 0 ? _e : parsed.input;
221995
+ if (val)
221996
+ return val;
221997
+ }
221998
+ catch (_f) {
221999
+ // Not every log snippet is JSON (apostrophes inside the payload break
222000
+ // the quote swap above). That is expected input, not an error — and
222001
+ // this runs inside a memo comparator on every streaming tick, so
222002
+ // logging here flooded the console on phones.
222003
+ }
222004
+ }
222005
+ }
222006
+ return null;
222007
+ }
222008
+ /** The `{...}` inside the first "with `{...}`" of a tool log, or null. */
222009
+ function extractLogPayload(log) {
222010
+ let from = 0;
222011
+ while (from < log.length) {
222012
+ const open = log.indexOf('`{', from);
222013
+ if (open === -1)
222014
+ return null;
222015
+ // Must follow the word "with" plus at least one whitespace character.
222016
+ let i = open - 1;
222017
+ let sawSpace = false;
222018
+ while (i >= 0 && /\s/.test(log[i])) {
222019
+ sawSpace = true;
222020
+ i -= 1;
222021
+ }
222022
+ if (sawSpace && log.slice(Math.max(0, i - 3), i + 1) === 'with') {
222023
+ const close = log.indexOf('}`', open + 1);
222024
+ if (close === -1)
222025
+ return null;
222026
+ return log.slice(open + 1, close + 1);
222027
+ }
222028
+ from = open + 1;
222029
+ }
222030
+ return null;
222031
+ }
222032
+ function formatResult(result) {
222033
+ return result.replace(/\n{3,}/g, '\n\n').trim();
222034
+ }
222035
+
222036
+ const TOOL_ICONS = {
222037
+ web_search_call: Globe,
222038
+ vector_search: Search,
222039
+ code_executor: Code$1,
222040
+ file_reader: FileText,
222041
+ wikipedia: BookOpen,
222042
+ };
222043
+ function getToolIcon(toolName) {
222044
+ return (toolName && TOOL_ICONS[toolName]) || Wrench;
222045
+ }
222046
+ /**
222047
+ * Collapsible record of the tools a turn used — the "Used N tools" line of
222048
+ * the chat bubble.
222049
+ *
222050
+ * The header always reads as the completed record ("Used N tools") and the
222051
+ * individual rows never animate. Only while this row is the live phase does the
222052
+ * header sprout dots — at which point the working line has stood down, so there
222053
+ * is still exactly one animated "agent is working" signal on screen.
222054
+ */
222055
+ function ToolCallIndicator({ toolCalls, isActive = false, showResults = false, }) {
222056
+ const [isOpen, setIsOpen] = useState(false);
222057
+ // `write_todos` has a dedicated renderer (`AgentTodoList`), so it must never
222058
+ // appear as a generic tool card nor be counted in "Used N tools".
222059
+ const visibleToolCalls = (toolCalls !== null && toolCalls !== void 0 ? toolCalls : []).filter((tc) => (tc === null || tc === void 0 ? void 0 : tc.name) !== WRITE_TODOS_TOOL);
222060
+ if (visibleToolCalls.length === 0) {
222061
+ return null;
222062
+ }
222063
+ const uniqueToolCount = new Set(visibleToolCalls.map((tc) => tc.name)).size;
222064
+ const headerLabel = `Used ${uniqueToolCount} tool${uniqueToolCount === 1 ? '' : 's'}`;
222065
+ return (jsxs(Collapsible, { open: isOpen, onOpenChange: setIsOpen, className: "mb-2", children: [jsxs(CollapsibleTrigger, { className: "flex cursor-pointer items-center gap-1 pt-1 text-xs text-gray-600 transition-colors hover:text-gray-800", children: [jsx(ChevronRight, { className: cn('h-3 w-3 transition-transform duration-200', isOpen && 'rotate-90') }), jsx(Wrench, { className: "h-3 w-3" }), jsx("span", { children: headerLabel }), isActive && (jsxs("span", { className: "inline-flex gap-0.5", "aria-hidden": "true", children: [jsx("span", { className: "inline-block h-1 w-1 animate-bounce rounded-full bg-gray-600 [animation-delay:0ms] motion-reduce:animate-none" }), jsx("span", { className: "inline-block h-1 w-1 animate-bounce rounded-full bg-gray-600 [animation-delay:150ms] motion-reduce:animate-none" }), jsx("span", { className: "inline-block h-1 w-1 animate-bounce rounded-full bg-gray-600 [animation-delay:300ms] motion-reduce:animate-none" })] }))] }), jsx(CollapsibleContent, { className: "pb-2 pl-4", children: jsx("div", { className: "space-y-2.5 border-l-2 border-gray-300 pt-2.5 pl-3 text-xs leading-relaxed text-gray-600", children: visibleToolCalls.map((toolCall, index) => {
222066
+ var _a;
222067
+ const query = getQueryLabel(toolCall);
222068
+ const Icon = getToolIcon(toolCall === null || toolCall === void 0 ? void 0 : toolCall.name);
222069
+ const result = showResults && (toolCall === null || toolCall === void 0 ? void 0 : toolCall.result) ? formatResult(toolCall.result) : null;
222070
+ return (jsxs("div", { children: [jsxs("div", { className: "flex items-center gap-1.5 text-gray-700", children: [jsx(Icon, { className: "h-3 w-3 shrink-0 text-gray-500" }), jsx("span", { className: "font-medium", children: getFriendlyToolName((_a = toolCall === null || toolCall === void 0 ? void 0 : toolCall.name) !== null && _a !== void 0 ? _a : '') })] }), query && (jsx("div", { className: "mt-0.5 ml-[18px] text-gray-600", children: jsx(Markdown, { className: "prose prose-xs max-w-none [&_*]:text-xs [&_*]:text-gray-600 [&_p]:m-0", children: query }) })), result && (jsx("div", { className: "mt-1 ml-[18px] max-h-48 overflow-y-auto rounded-md border border-gray-200 bg-white px-2 py-1.5 leading-relaxed whitespace-pre-wrap text-gray-600", "data-testid": "tool-call-result", children: result }))] }, (toolCall === null || toolCall === void 0 ? void 0 : toolCall.id) || index));
222071
+ }) }) })] }));
222072
+ }
222073
+
222074
+ // Model wire keys whose raw form is not presentable, keyed lowercased and
222075
+ // alphanumerics-only.
222076
+ const IBL_AI_DISPLAY_NAME = 'ibl.ai';
222077
+ const MODEL_DISPLAY_NAME_BY_KEY = {
222078
+ iblai: IBL_AI_DISPLAY_NAME,
222079
+ };
222080
+ /**
222081
+ * The label to show for a model when only its wire key is known.
222082
+ *
222083
+ * The LLM picker gets a `display_name` per model straight from the API, but
222084
+ * mentor settings and transcripts persist only `llm_name` — a wire key — so
222085
+ * surfaces that read from those have no label to fall back on and would
222086
+ * render `iblai`. Most keys are already presentable (`gpt-4.1`), so this maps
222087
+ * only the ones that are not and passes everything else through untouched.
222088
+ */
222089
+ function getLLMModelDisplayName(llmName) {
222090
+ var _a;
222091
+ const raw = llmName !== null && llmName !== void 0 ? llmName : '';
222092
+ const normalized = raw.toLowerCase().replace(/[^a-z0-9]/g, '');
222093
+ // ibl.ai keys are the brand followed by an optional variant — `iblai`,
222094
+ // `iblai-pro`, `iblai-fast`, `IBL AI Pro`. Rebuild the label the model
222095
+ // picker gets from the API (`ibl.ai Pro`) rather than collapsing every
222096
+ // variant onto a bare `ibl.ai`, which would disagree with the picker.
222097
+ const brand = /^(iblai|ibl)(.*)$/.exec(normalized);
222098
+ const leadsWithBrand = brand &&
222099
+ (brand[1] === 'iblai' ||
222100
+ raw
222101
+ .toLowerCase()
222102
+ .split(/[^a-z0-9]+/)
222103
+ .filter(Boolean)[0] === 'ibl');
222104
+ if (leadsWithBrand) {
222105
+ const variant = brand[2];
222106
+ if (!variant)
222107
+ return IBL_AI_DISPLAY_NAME;
222108
+ // `ai` is part of the brand when the key spells it out (`ibl-ai-pro`).
222109
+ const suffix = variant.startsWith('ai') && brand[1] === 'ibl' ? variant.slice(2) : variant;
222110
+ if (!suffix)
222111
+ return IBL_AI_DISPLAY_NAME;
222112
+ return `${IBL_AI_DISPLAY_NAME} ${suffix.charAt(0).toUpperCase()}${suffix.slice(1)}`;
222113
+ }
222114
+ return (_a = MODEL_DISPLAY_NAME_BY_KEY[normalized]) !== null && _a !== void 0 ? _a : raw;
222115
+ }
222116
+
222117
+ /** Loose key for matching a model across spellings: lower-case, alphanumerics only. */
222118
+ const modelKey = (value) => (value !== null && value !== void 0 ? value : '').toLowerCase().replace(/[^a-z0-9]/g, '');
222119
+ /**
222120
+ * Model families → the provider keys the catalogue may list them under, for
222121
+ * a model the catalogue does not name (a dated or "-latest" wire key, or a
222122
+ * model the tenant enabled elsewhere). First key present in the catalogue
222123
+ * wins; hosted variants (Azure, Bedrock, Groq) come after the vendor.
222124
+ */
222125
+ const MODEL_FAMILY_PROVIDERS = [
222126
+ [/^(gpt|o[1-9]|chatgpt|davinci|text-embedding)/, ['openai', 'azure_openai', 'azure']],
222127
+ [/^claude/, ['anthropic', 'bedrock']],
222128
+ [/^(gemini|gemma|palm)/, ['google', 'vertex', 'vertexai', 'gemini']],
222129
+ [/^(llama|meta)/, ['llama', 'meta', 'bedrock', 'groq']],
222130
+ [/^(mistral|mixtral|codestral)/, ['mistral']],
222131
+ [/^deepseek/, ['deepseek']],
222132
+ [/^grok/, ['xai', 'grok']],
222133
+ [/^(command|aya)/, ['cohere']],
222134
+ [/^qwen/, ['qwen', 'alibaba']],
222135
+ [/^(nova|titan)/, ['bedrock', 'amazon']],
222136
+ ];
222137
+ /** Provider keys a model of this family is listed under, in order of preference. */
222138
+ function inferProviderKeysFromModel(model) {
222139
+ const key = modelKey(model);
222140
+ if (!key)
222141
+ return [];
222142
+ for (const [pattern, providers] of MODEL_FAMILY_PROVIDERS) {
222143
+ if (pattern.test(key))
222144
+ return providers;
222145
+ }
222146
+ return [];
222147
+ }
222148
+ /**
222149
+ * A resolver from a provider key (the `llm_provider` mentor settings persist,
222150
+ * e.g. `openai`) to the name and logo the backend publishes for it. A second,
222151
+ * optional model key finds the provider whose `chat_models` list it, for
222152
+ * payloads that name only the model (analytics transcript rows).
222153
+ *
222154
+ * Provider naming is backend-owned: the mentor-llms catalogue carries each
222155
+ * provider's `logo` (and `display_name` where the API supplies one), so this
222156
+ * reads the same RTK Query cache entry the LLM tab and model picker fill —
222157
+ * matched on `name`. A key the catalogue does not list (the catalogue is
222158
+ * still loading, or an on-device provider that has no backend row) resolves
222159
+ * to the raw key and no logo, so callers degrade to plain text.
222160
+ *
222161
+ * Returns a function rather than a value so list renderers (a mentors table,
222162
+ * a facet dropdown) can resolve many keys from one subscription.
222163
+ */
222164
+ function useLlmProviderCatalogue({ org, userId, mentorId }) {
222165
+ const skip = !org || !userId || !mentorId;
222166
+ const { data: providers } = useGetLlmsQuery({
222167
+ org: org !== null && org !== void 0 ? org : '',
222168
+ // @ts-expect-error - userId is not in the generated query args but the endpoint scopes by it
222169
+ userId: userId !== null && userId !== void 0 ? userId : '',
222170
+ mentorId: mentorId !== null && mentorId !== void 0 ? mentorId : '',
222171
+ }, { skip });
222172
+ // Indexed once per catalogue load so list renderers resolve each key in
222173
+ // O(1). First row wins on a duplicate name, as `Array.find` would.
222174
+ const rowsByName = useMemo(() => {
222175
+ const byName = new Map();
222176
+ if (!Array.isArray(providers))
222177
+ return byName;
222178
+ for (const row of providers) {
222179
+ if ((row === null || row === void 0 ? void 0 : row.name) && !byName.has(row.name))
222180
+ byName.set(row.name, row);
222181
+ }
222182
+ return byName;
222183
+ }, [providers]);
222184
+ // Model key → the provider row listing it. Keyed loosely (see `modelKey`):
222185
+ // transcripts persist the model as it was displayed ("GPT-5.2") while the
222186
+ // catalogue lists the wire key ("gpt-5.2"), so both spellings, and the
222187
+ // display name, must find the row.
222188
+ const rowsByModel = useMemo(() => {
222189
+ var _a;
222190
+ const byModel = new Map();
222191
+ for (const row of rowsByName.values()) {
222192
+ for (const model of (_a = row.chat_models) !== null && _a !== void 0 ? _a : []) {
222193
+ for (const candidate of [model === null || model === void 0 ? void 0 : model.llm_name, model === null || model === void 0 ? void 0 : model.display_name]) {
222194
+ const key = modelKey(candidate);
222195
+ if (key && !byModel.has(key))
222196
+ byModel.set(key, row);
222197
+ }
222198
+ }
222199
+ }
222200
+ return byModel;
222201
+ }, [rowsByName]);
222202
+ // Provider rows keyed loosely by name and display name ("OpenAI" → openai).
222203
+ const rowsByLooseName = useMemo(() => {
222204
+ const byLoose = new Map();
222205
+ for (const row of rowsByName.values()) {
222206
+ for (const candidate of [row.name, row.display_name]) {
222207
+ const key = modelKey(candidate);
222208
+ if (key && !byLoose.has(key))
222209
+ byLoose.set(key, row);
222210
+ }
222211
+ }
222212
+ return byLoose;
222213
+ }, [rowsByName]);
222214
+ const rowForModel = useCallback((llmName) => {
222215
+ var _a;
222216
+ const wanted = modelKey(llmName);
222217
+ if (!wanted)
222218
+ return undefined;
222219
+ const exact = rowsByModel.get(wanted);
222220
+ if (exact)
222221
+ return exact;
222222
+ // A dated or "-latest" wire key ("gpt-5.2-2026-03-01") still names
222223
+ // the family the catalogue lists ("gpt-5.2"), and vice versa.
222224
+ for (const [listed, row] of rowsByModel) {
222225
+ if (listed.startsWith(wanted) || wanted.startsWith(listed))
222226
+ return row;
222227
+ }
222228
+ for (const provider of inferProviderKeysFromModel(llmName)) {
222229
+ const row = (_a = rowsByName.get(provider)) !== null && _a !== void 0 ? _a : rowsByLooseName.get(modelKey(provider));
222230
+ if (row)
222231
+ return row;
222232
+ }
222233
+ return undefined;
222234
+ }, [rowsByModel, rowsByName, rowsByLooseName]);
222235
+ return useCallback((llmProvider, llmName) => {
222236
+ var _a, _b, _c, _d, _e;
222237
+ const key = llmProvider !== null && llmProvider !== void 0 ? llmProvider : '';
222238
+ const row = (_c = (_b = (_a = rowsByName.get(key)) !== null && _a !== void 0 ? _a : rowsByName.get(key.toLowerCase())) !== null && _b !== void 0 ? _b : rowsByLooseName.get(modelKey(key))) !== null && _c !== void 0 ? _c : rowForModel(llmName);
222239
+ return {
222240
+ logo: (row === null || row === void 0 ? void 0 : row.logo) || null,
222241
+ displayName: ((_d = row === null || row === void 0 ? void 0 : row.display_name) === null || _d === void 0 ? void 0 : _d.trim()) || ((_e = row === null || row === void 0 ? void 0 : row.name) === null || _e === void 0 ? void 0 : _e.trim()) || key,
222242
+ };
222243
+ }, [rowsByName, rowsByLooseName, rowForModel]);
222244
+ }
222245
+ const clean$1 = (value) => (value === null || value === void 0 ? void 0 : value.trim()) || undefined;
222246
+ /**
222247
+ * The label to show for a model.
222248
+ *
222249
+ * Settings persist only `llm_name` — a wire key like `iblai-pro` or
222250
+ * `claude-haiku-4-5-20251001` — while the model picker reads the catalogue,
222251
+ * which carries a `display_name` ("ibl.ai Pro", "Claude Haiku 4.5"). Resolve
222252
+ * the label in three steps:
222253
+ *
222254
+ * 1. `llm_config.display_name` from settings, when the caller has it.
222255
+ * 2. The mentor-llms catalogue, matched on `llm_name`. Only queried when step
222256
+ * 1 came back empty.
222257
+ * 3. `getLLMModelDisplayName`, which tidies the handful of keys whose raw
222258
+ * form is unpresentable — used while the catalogue is loading or failed.
222259
+ */
222260
+ function useLlmDisplayName({ llmName, llmConfig, org, userId, mentorId, }) {
222261
+ var _a;
222262
+ const fromConfig = clean$1(llmConfig === null || llmConfig === void 0 ? void 0 : llmConfig.display_name);
222263
+ const skip = Boolean(fromConfig) || !llmName || !org || !userId || !mentorId;
222264
+ const { data: providers } = useGetLlmsQuery({
222265
+ org: org !== null && org !== void 0 ? org : '',
222266
+ // @ts-expect-error - userId is not in the generated query args but the endpoint scopes by it
222267
+ userId: userId !== null && userId !== void 0 ? userId : '',
222268
+ mentorId: mentorId !== null && mentorId !== void 0 ? mentorId : '',
222269
+ }, { skip });
222270
+ const fromCatalogue = useMemo(() => {
222271
+ var _a;
222272
+ if (skip || !llmName || !Array.isArray(providers))
222273
+ return undefined;
222274
+ const wanted = modelKey(llmName);
222275
+ for (const provider of providers) {
222276
+ const match = (_a = provider === null || provider === void 0 ? void 0 : provider.chat_models) === null || _a === void 0 ? void 0 : _a.find((model) => modelKey(model === null || model === void 0 ? void 0 : model.llm_name) === wanted || modelKey(model === null || model === void 0 ? void 0 : model.display_name) === wanted);
222277
+ const label = clean$1(match === null || match === void 0 ? void 0 : match.display_name);
222278
+ if (label)
222279
+ return label;
222280
+ }
222281
+ return undefined;
222282
+ }, [skip, providers, llmName]);
222283
+ return (_a = fromConfig !== null && fromConfig !== void 0 ? fromConfig : fromCatalogue) !== null && _a !== void 0 ? _a : getLLMModelDisplayName(llmName);
222284
+ }
222285
+
222286
+ /**
222287
+ * A model the way the nav bar shows the mentor's: the provider logo in a
222288
+ * white circle (the catalogue's artwork, or the bot icon when it has none)
222289
+ * beside the model's display name. Read-only; used on admin transcripts to
222290
+ * name the model a turn ran on.
222291
+ */
222292
+ function LlmModelBadge({ model, provider, tenantKey, mentorId, className, }) {
222293
+ var _a, _b;
222294
+ const params = useParams();
222295
+ const username = useUsername();
222296
+ const org = (_a = tenantKey !== null && tenantKey !== void 0 ? tenantKey : params === null || params === void 0 ? void 0 : params.tenantKey) !== null && _a !== void 0 ? _a : null;
222297
+ const mentor = (_b = mentorId !== null && mentorId !== void 0 ? mentorId : params === null || params === void 0 ? void 0 : params.mentorId) !== null && _b !== void 0 ? _b : null;
222298
+ const resolveLlmProvider = useLlmProviderCatalogue({ org, userId: username, mentorId: mentor });
222299
+ // With no provider key (transcript rows carry only the model), the
222300
+ // catalogue row that lists this model supplies the logo and name.
222301
+ const providerDetails = resolveLlmProvider(provider, model);
222302
+ const displayName = useLlmDisplayName({
222303
+ llmName: model,
222304
+ org,
222305
+ userId: username,
222306
+ mentorId: mentor,
222307
+ });
222308
+ const label = displayName || providerDetails.displayName;
222309
+ if (!label)
222310
+ return null;
222311
+ return (jsxs("span", { className: cn('inline-flex max-w-full items-center gap-2 text-sm font-medium text-[#646464]', className), title: providerDetails.displayName && providerDetails.displayName !== label
222312
+ ? `${label} · ${providerDetails.displayName}`
222313
+ : label, "data-testid": "llm-model-badge", children: [jsx("span", { className: "flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-white", children: providerDetails.logo ? (jsx("img", { src: providerDetails.logo, alt: `${label} model logo`, className: "h-5 w-5 object-contain", height: 32, width: 32, loading: "lazy" })) : (jsx(Bot, { className: "h-4 w-4", "aria-hidden": "true" })) }), jsx("span", { className: "max-w-[220px] truncate", children: label })] }));
222314
+ }
222315
+
222316
+ /** SPAs whose users have coursework, so the profile leads with a Gradebook tab. */
222317
+ const GRADEBOOK_SPAS = ['lms', 'skills'];
222318
+ /**
222319
+ * The customization `Profile` is opened with: whatever the host passed, with
222320
+ * the Gradebook tab defaulted from the SPA it runs in. An explicit
222321
+ * `showGradebookTab` always wins, so a host can force it either way.
222322
+ */
222323
+ function profileCustomization(customization, currentSPA) {
222324
+ var _a;
222325
+ return {
222326
+ ...customization,
222327
+ showGradebookTab: (_a = customization === null || customization === void 0 ? void 0 : customization.showGradebookTab) !== null && _a !== void 0 ? _a : GRADEBOOK_SPAS.includes((currentSPA !== null && currentSPA !== void 0 ? currentSPA : '').trim().toLowerCase()),
222328
+ };
222329
+ }
222330
+ /**
222331
+ * The shared `Profile` viewer in a dialog — the same modal the tenant
222332
+ * Management tab opens when a user row is clicked. Controlled variant for
222333
+ * hosts that trigger it from a whole table row.
222334
+ *
222335
+ * Radix portals the dialog into `document.body`, but a React portal still
222336
+ * bubbles its events through the React tree — so without the guard below, a
222337
+ * click *inside* the open profile would reach the row this dialog is rendered
222338
+ * from and fire its `onClick` (e.g. opening the enrollment progress modal
222339
+ * behind the profile). The wrapper is `display: contents`, so it adds nothing
222340
+ * to the host's layout.
222341
+ */
222342
+ function UserProfileDialog({ tenantKey, username, isOpen, onClose, customization, currentSPA, }) {
222343
+ const t = useT();
222344
+ if (!isOpen)
222345
+ return null;
222346
+ return (jsx("span", { className: "contents", onClick: (event) => event.stopPropagation(), onMouseDown: (event) => event.stopPropagation(), onKeyDown: (event) => event.stopPropagation(), children: jsx(Dialog, { open: true, onOpenChange: onClose, children: jsxs(DialogContent, { autoFocus: false, className: "profile-component-dialog max-w-7xl w-[85vw] gap-0 overflow-hidden bg-white p-0", style: { height: '85vh', display: 'flex', flexDirection: 'column' }, "data-testid": "user-profile-link-dialog", children: [jsx(DialogTitle, { className: "sr-only", children: t('tabsUserstab.profileDialogTitle') }), jsx(DialogDescription, { className: "sr-only", children: t('tabsUserstab.profileDialogDescription') }), jsx(Profile, { tenant: tenantKey, tenants: [], username: username, onClose: onClose, customization: profileCustomization(customization, currentSPA), currentSPA: currentSPA })] }) }) }));
222347
+ }
222348
+ /**
222349
+ * Clickable user identity that opens the shared `Profile` viewer in a dialog
222350
+ * — the same modal the tenant Management tab opens when a user row is
222351
+ * clicked. `Profile` drops into preview (read-only) mode on its own for anyone
222352
+ * but the signed-in user. Used by the spend-cap tables and every analytics
222353
+ * surface that names a user; safe inside clickable rows and cards, since
222354
+ * neither its clicks nor its key presses reach the host's handlers.
222355
+ */
222356
+ function UserProfileLink({ tenantKey, username, display, className, testId, customization, currentSPA, }) {
222357
+ const t = useT();
222358
+ const [isOpen, setIsOpen] = React__default.useState(false);
222359
+ if (!username) {
222360
+ // `inline-block`, or `truncate`'s `overflow: hidden` has nothing to clip:
222361
+ // an inline box takes its width from its own text.
222362
+ return (jsx("span", { className: cn('inline-block max-w-full truncate', className), "data-testid": testId, children: display }));
222363
+ }
222364
+ return (jsxs(Fragment$1, { children: [jsx("button", { type: "button", onClick: (event) => {
222365
+ event.stopPropagation();
222366
+ setIsOpen(true);
222367
+ },
222368
+ // Rows that open on Enter/Space would otherwise swallow the key press.
222369
+ onKeyDown: (event) => event.stopPropagation(), className: cn('max-w-full cursor-pointer truncate text-left hover:text-blue-600 hover:underline focus:text-blue-600 focus:outline-none', className), "aria-label": t('tabsUserstab.viewProfileAriaLabel', { name: display }), "data-testid": testId, children: display }), jsx(UserProfileDialog, { tenantKey: tenantKey, username: username, isOpen: isOpen, onClose: () => setIsOpen(false), customization: customization, currentSPA: currentSPA })] }));
222370
+ }
222371
+
220520
222372
  const AdvancedPagination = ({ currentPage, totalPages, onPageChange, totalCount, itemsPerPage, itemLabel = 'items', variant = 'default', className, }) => {
220521
222373
  const t = useT();
220522
222374
  if (totalPages <= 1) {
@@ -222427,141 +224279,6 @@ function trimTrailingSlashes(value) {
222427
224279
  return end === value.length ? value : value.slice(0, end);
222428
224280
  }
222429
224281
 
222430
- var COLLAPSIBLE_NAME = "Collapsible";
222431
- var [createCollapsibleContext] = createContextScope$1(COLLAPSIBLE_NAME);
222432
- var [CollapsibleProvider, useCollapsibleContext] = createCollapsibleContext(COLLAPSIBLE_NAME);
222433
- var Collapsible$1 = React.forwardRef(
222434
- (props, forwardedRef) => {
222435
- const {
222436
- __scopeCollapsible,
222437
- open: openProp,
222438
- defaultOpen,
222439
- disabled,
222440
- onOpenChange,
222441
- ...collapsibleProps
222442
- } = props;
222443
- const [open, setOpen] = useControllableState$1({
222444
- prop: openProp,
222445
- defaultProp: defaultOpen ?? false,
222446
- onChange: onOpenChange,
222447
- caller: COLLAPSIBLE_NAME
222448
- });
222449
- return /* @__PURE__ */ jsx(
222450
- CollapsibleProvider,
222451
- {
222452
- scope: __scopeCollapsible,
222453
- disabled,
222454
- contentId: useId$1(),
222455
- open,
222456
- onOpenToggle: React.useCallback(() => setOpen((prevOpen) => !prevOpen), [setOpen]),
222457
- children: /* @__PURE__ */ jsx(
222458
- Primitive$2.div,
222459
- {
222460
- "data-state": getState$1(open),
222461
- "data-disabled": disabled ? "" : void 0,
222462
- ...collapsibleProps,
222463
- ref: forwardedRef
222464
- }
222465
- )
222466
- }
222467
- );
222468
- }
222469
- );
222470
- Collapsible$1.displayName = COLLAPSIBLE_NAME;
222471
- var TRIGGER_NAME$2 = "CollapsibleTrigger";
222472
- var CollapsibleTrigger$1 = React.forwardRef(
222473
- (props, forwardedRef) => {
222474
- const { __scopeCollapsible, ...triggerProps } = props;
222475
- const context = useCollapsibleContext(TRIGGER_NAME$2, __scopeCollapsible);
222476
- return /* @__PURE__ */ jsx(
222477
- Primitive$2.button,
222478
- {
222479
- type: "button",
222480
- "aria-controls": context.contentId,
222481
- "aria-expanded": context.open || false,
222482
- "data-state": getState$1(context.open),
222483
- "data-disabled": context.disabled ? "" : void 0,
222484
- disabled: context.disabled,
222485
- ...triggerProps,
222486
- ref: forwardedRef,
222487
- onClick: composeEventHandlers$1(props.onClick, context.onOpenToggle)
222488
- }
222489
- );
222490
- }
222491
- );
222492
- CollapsibleTrigger$1.displayName = TRIGGER_NAME$2;
222493
- var CONTENT_NAME$3 = "CollapsibleContent";
222494
- var CollapsibleContent$1 = React.forwardRef(
222495
- (props, forwardedRef) => {
222496
- const { forceMount, ...contentProps } = props;
222497
- const context = useCollapsibleContext(CONTENT_NAME$3, props.__scopeCollapsible);
222498
- return /* @__PURE__ */ jsx(Presence$2, { present: forceMount || context.open, children: ({ present }) => /* @__PURE__ */ jsx(CollapsibleContentImpl, { ...contentProps, ref: forwardedRef, present }) });
222499
- }
222500
- );
222501
- CollapsibleContent$1.displayName = CONTENT_NAME$3;
222502
- var CollapsibleContentImpl = React.forwardRef((props, forwardedRef) => {
222503
- const { __scopeCollapsible, present, children, ...contentProps } = props;
222504
- const context = useCollapsibleContext(CONTENT_NAME$3, __scopeCollapsible);
222505
- const [isPresent, setIsPresent] = React.useState(present);
222506
- const ref = React.useRef(null);
222507
- const composedRefs = useComposedRefs$1(forwardedRef, ref);
222508
- const heightRef = React.useRef(0);
222509
- const height = heightRef.current;
222510
- const widthRef = React.useRef(0);
222511
- const width = widthRef.current;
222512
- const isOpen = context.open || isPresent;
222513
- const isMountAnimationPreventedRef = React.useRef(isOpen);
222514
- const originalStylesRef = React.useRef(void 0);
222515
- React.useEffect(() => {
222516
- const rAF = requestAnimationFrame(() => isMountAnimationPreventedRef.current = false);
222517
- return () => cancelAnimationFrame(rAF);
222518
- }, []);
222519
- useLayoutEffect2$1(() => {
222520
- const node = ref.current;
222521
- if (node) {
222522
- originalStylesRef.current = originalStylesRef.current || {
222523
- transitionDuration: node.style.transitionDuration,
222524
- animationName: node.style.animationName
222525
- };
222526
- node.style.transitionDuration = "0s";
222527
- node.style.animationName = "none";
222528
- const rect = node.getBoundingClientRect();
222529
- heightRef.current = rect.height;
222530
- widthRef.current = rect.width;
222531
- if (!isMountAnimationPreventedRef.current) {
222532
- node.style.transitionDuration = originalStylesRef.current.transitionDuration;
222533
- node.style.animationName = originalStylesRef.current.animationName;
222534
- }
222535
- setIsPresent(present);
222536
- }
222537
- }, [context.open, present]);
222538
- return /* @__PURE__ */ jsx(
222539
- Primitive$2.div,
222540
- {
222541
- "data-state": getState$1(context.open),
222542
- "data-disabled": context.disabled ? "" : void 0,
222543
- id: context.contentId,
222544
- hidden: !isOpen,
222545
- ...contentProps,
222546
- ref: composedRefs,
222547
- style: {
222548
- [`--radix-collapsible-content-height`]: height ? `${height}px` : void 0,
222549
- [`--radix-collapsible-content-width`]: width ? `${width}px` : void 0,
222550
- ...props.style
222551
- },
222552
- children: isOpen && children
222553
- }
222554
- );
222555
- });
222556
- function getState$1(open) {
222557
- return open ? "open" : "closed";
222558
- }
222559
- var Root$5 = Collapsible$1;
222560
-
222561
- const Collapsible = Root$5;
222562
- const CollapsibleTrigger = CollapsibleTrigger$1;
222563
- const CollapsibleContent = CollapsibleContent$1;
222564
-
222565
224282
  // Pure logic behind the application form (questions) editor. No React here so
222566
224283
  // it stays unit testable. The backend validates schemas on save and remains
222567
224284
  // authoritative; these helpers only shape the editor's local state.
@@ -226185,19 +227902,6 @@ function AddCreditsModal({ isOpen, onClose, tenant }) {
226185
227902
  return (jsx(Dialog, { open: isOpen, onOpenChange: handleClose, children: jsxs(DialogContent, { className: "max-w-md", children: [jsxs(DialogHeader, { children: [jsx(DialogTitle, { children: t('profileAddCreditsModal.title') }), jsx(DialogDescription, { children: t('profileAddCreditsModal.description') })] }), jsxs("form", { onSubmit: handleSubmit, className: "space-y-6", children: [jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "credit-amount", className: "text-sm font-medium", children: t('profileAddCreditsModal.amountLabel') }), jsxs("div", { className: "relative", children: [jsx(DollarSign, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-slate-400" }), jsx(Input, { id: "credit-amount", type: "number", min: "0.01", step: "0.01", placeholder: "50.00", value: amount, onChange: (e) => setAmount(e.target.value), className: "pl-9" })] }), jsx("p", { className: "text-xs text-slate-500 dark:text-slate-400", children: t('profileAddCreditsModal.amountHint') })] }), jsxs("div", { className: "flex items-start gap-2 p-3 bg-blue-50 dark:bg-blue-900/20 rounded-lg border border-blue-100 dark:border-blue-800", children: [jsx(CircleAlert, { className: "h-4 w-4 text-blue-500 mt-0.5 flex-shrink-0" }), jsx("p", { className: "text-xs text-blue-700 dark:text-blue-300", children: t('profileAddCreditsModal.paymentMethodNotice') })] }), jsxs(DialogFooter, { className: "gap-2 sm:gap-0", children: [jsx(Button$1, { type: "button", variant: "outline", onClick: handleClose, children: t('profileAddCreditsModal.cancel') }), jsx(Button$1, { type: "submit", disabled: isLoading, className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] hover:opacity-90 text-white", children: isLoading ? (jsxs(Fragment$1, { children: [jsx(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), t('profileAddCreditsModal.processing')] })) : (t('profileAddCreditsModal.addCredits')) })] })] })] }) }));
226186
227903
  }
226187
227904
 
226188
- /**
226189
- * The info icon that sits on every analytics card and, on hover or keyboard
226190
- * focus, explains what the card measures. The description doubles as the
226191
- * accessible name so screen readers get it without needing the hover.
226192
- *
226193
- * Uses the tooltip primitives directly rather than the shared `TooltipContent`
226194
- * because analytics wants a light popover, while `TooltipContent` is the dark
226195
- * `bg-primary` style the rest of the app relies on.
226196
- */
226197
- function CardInfoIcon({ title, description, className }) {
226198
- return (jsxs(Tooltip$1, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx("button", { type: "button", "aria-label": description, className: cn('shrink-0 cursor-help rounded p-1 text-[#898989] transition-colors hover:bg-gray-100 hover:text-[#38A1E5] focus-visible:ring-2 focus-visible:ring-[#38A1E5] focus-visible:outline-none', className), children: jsx(Info$3, { className: "h-4 w-4", "aria-hidden": "true" }) }) }), jsx(Portal$2, { children: jsxs(Content2$3, { "data-slot": "tooltip-content", side: "top", align: "center", sideOffset: 6, collisionPadding: 12, className: "animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 z-50 max-w-[260px] rounded-lg border border-[oklch(0.922_0_0)] bg-white p-3 shadow-lg", children: [title && (jsx("div", { "data-slot": "tooltip-title", className: "mb-1 text-sm font-semibold text-[#484848]", children: title })), jsx("p", { "data-slot": "tooltip-description", className: "text-sm leading-relaxed text-[#646464]", children: description })] }) })] }));
226199
- }
226200
-
226201
227905
  /**
226202
227906
  * Shared utilities for tab label systems.
226203
227907
  *
@@ -226780,62 +228484,6 @@ function SpendLimitsSection({ tenantKey, labels: labelsOverride }) {
226780
228484
  return (jsxs(Fragment$1, { children: [jsxs("div", { "data-testid": "spend-limits-workspace-section", className: "rounded-lg border border-[#e5e5e5] bg-white shadow-[0_1px_2px_0_rgba(0,0,0,0.05)] dark:bg-zinc-900 dark:border-zinc-800", children: [jsx("div", { className: "flex flex-col gap-2 p-4 sm:flex-row sm:items-start sm:justify-between", children: jsxs("div", { className: "space-y-0.5", children: [jsx("h3", { className: "text-[16px] font-medium leading-6 text-[#101828] dark:text-gray-100", children: labels.tenantSections.workspace.title }), jsx("p", { className: "text-[13px] font-normal leading-5 text-[#4a5565] dark:text-gray-400", children: labels.tenantSections.workspace.description })] }) }), jsx("div", { className: "border-t border-[#e5e7eb] dark:border-zinc-800 px-4 py-3", children: isTenantCapLoading ? (jsx("div", { className: "flex items-center justify-center py-8", "data-testid": "tenant-cap-loading", children: jsx(Spinner, {}) })) : isTenantForbidden ? (jsxs("div", { className: "flex flex-col items-center gap-2 rounded-lg border border-gray-200 bg-gray-50 px-4 py-10 text-center", "data-testid": "spend-limits-denied", children: [jsx(ShieldAlert, { className: "h-6 w-6 text-gray-400" }), jsx("p", { className: "text-sm font-medium text-gray-900", children: labels.denied.title }), jsx("p", { className: "max-w-sm text-xs text-gray-600", children: labels.denied.description })] })) : (jsxs("div", { className: "space-y-4", children: [!tenantCap && (monthlySpend != null || totalSpend != null) && (jsxs("div", { className: "space-y-1", "data-testid": "workspace-actual-spend-stats", children: [jsxs("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2", children: [monthlySpend != null && (jsxs("div", { className: "space-y-0.5", children: [jsx("p", { className: "text-[11px] font-medium uppercase tracking-wide text-[#6a7282] dark:text-gray-400", children: labels.tenantSections.noCapStats.spentThisMonth }), jsx("p", { className: "text-[18px] font-semibold leading-6 text-[#101828] dark:text-gray-100", "data-testid": "workspace-actual-spend-month", children: formatUsd$1(String(monthlySpend)) })] })), totalSpend != null && (jsxs("div", { className: "space-y-0.5", children: [jsx("p", { className: "text-[11px] font-medium uppercase tracking-wide text-[#6a7282] dark:text-gray-400", children: labels.tenantSections.noCapStats.spentAllTime }), jsx("p", { className: "text-[18px] font-semibold leading-6 text-[#101828] dark:text-gray-100", "data-testid": "workspace-actual-spend-total", children: formatUsd$1(String(totalSpend)) })] }))] }), jsx("p", { className: "text-[12px] font-normal leading-4 text-[#6a7282] dark:text-gray-500", children: labels.tenantSections.noCapStats.hint })] })), jsx(SpendCapForm, { labels: labels, cap: tenantCap, isSaving: isSavingTenantCap, isDeleting: isDeletingTenantCap, onSave: saveTenantCap, onDelete: () => setIsDeleteOpen(true), testId: "tenant-cap" })] })) })] }), jsx(Dialog, { open: isDeleteOpen, onOpenChange: () => setIsDeleteOpen(false), children: jsxs(DialogContent, { "aria-describedby": "tenant-cap-delete-description", "data-testid": "tenant-cap-delete-modal", children: [jsx(DialogHeader, { children: jsx(DialogTitle, { className: "ibl-dialog-title", children: labels.deleteModal.title }) }), jsx("div", { className: "my-5", id: "tenant-cap-delete-description", children: jsx("p", { className: "text-sm text-[#646464]", children: labels.deleteModal.description }) }), jsxs(DialogFooter, { className: "gap-3", children: [jsx(Button$1, { variant: "outline", onClick: () => setIsDeleteOpen(false), children: labels.deleteModal.cancel }), jsx(Button$1, { className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", onClick: handleConfirmDelete, disabled: isDeletingTenantCap, "data-testid": "tenant-cap-delete-confirm", children: isDeletingTenantCap ? labels.form.removing : labels.deleteModal.confirm })] })] }) })] }));
226781
228485
  }
226782
228486
 
226783
- /** SPAs whose users have coursework, so the profile leads with a Gradebook tab. */
226784
- const GRADEBOOK_SPAS = ['lms', 'skills'];
226785
- /**
226786
- * The customization `Profile` is opened with: whatever the host passed, with
226787
- * the Gradebook tab defaulted from the SPA it runs in. An explicit
226788
- * `showGradebookTab` always wins, so a host can force it either way.
226789
- */
226790
- function profileCustomization(customization, currentSPA) {
226791
- var _a;
226792
- return {
226793
- ...customization,
226794
- showGradebookTab: (_a = customization === null || customization === void 0 ? void 0 : customization.showGradebookTab) !== null && _a !== void 0 ? _a : GRADEBOOK_SPAS.includes((currentSPA !== null && currentSPA !== void 0 ? currentSPA : '').trim().toLowerCase()),
226795
- };
226796
- }
226797
- /**
226798
- * The shared `Profile` viewer in a dialog — the same modal the tenant
226799
- * Management tab opens when a user row is clicked. Controlled variant for
226800
- * hosts that trigger it from a whole table row.
226801
- *
226802
- * Radix portals the dialog into `document.body`, but a React portal still
226803
- * bubbles its events through the React tree — so without the guard below, a
226804
- * click *inside* the open profile would reach the row this dialog is rendered
226805
- * from and fire its `onClick` (e.g. opening the enrollment progress modal
226806
- * behind the profile). The wrapper is `display: contents`, so it adds nothing
226807
- * to the host's layout.
226808
- */
226809
- function UserProfileDialog({ tenantKey, username, isOpen, onClose, customization, currentSPA, }) {
226810
- const t = useT();
226811
- if (!isOpen)
226812
- return null;
226813
- return (jsx("span", { className: "contents", onClick: (event) => event.stopPropagation(), onMouseDown: (event) => event.stopPropagation(), onKeyDown: (event) => event.stopPropagation(), children: jsx(Dialog, { open: true, onOpenChange: onClose, children: jsxs(DialogContent, { autoFocus: false, className: "profile-component-dialog max-w-7xl w-[85vw] gap-0 overflow-hidden bg-white p-0", style: { height: '85vh', display: 'flex', flexDirection: 'column' }, "data-testid": "user-profile-link-dialog", children: [jsx(DialogTitle, { className: "sr-only", children: t('tabsUserstab.profileDialogTitle') }), jsx(DialogDescription, { className: "sr-only", children: t('tabsUserstab.profileDialogDescription') }), jsx(Profile, { tenant: tenantKey, tenants: [], username: username, onClose: onClose, customization: profileCustomization(customization, currentSPA), currentSPA: currentSPA })] }) }) }));
226814
- }
226815
- /**
226816
- * Clickable user identity that opens the shared `Profile` viewer in a dialog
226817
- * — the same modal the tenant Management tab opens when a user row is
226818
- * clicked. `Profile` drops into preview (read-only) mode on its own for anyone
226819
- * but the signed-in user. Used by the spend-cap tables and every analytics
226820
- * surface that names a user; safe inside clickable rows and cards, since
226821
- * neither its clicks nor its key presses reach the host's handlers.
226822
- */
226823
- function UserProfileLink({ tenantKey, username, display, className, testId, customization, currentSPA, }) {
226824
- const t = useT();
226825
- const [isOpen, setIsOpen] = React__default.useState(false);
226826
- if (!username) {
226827
- // `inline-block`, or `truncate`'s `overflow: hidden` has nothing to clip:
226828
- // an inline box takes its width from its own text.
226829
- return (jsx("span", { className: cn('inline-block max-w-full truncate', className), "data-testid": testId, children: display }));
226830
- }
226831
- return (jsxs(Fragment$1, { children: [jsx("button", { type: "button", onClick: (event) => {
226832
- event.stopPropagation();
226833
- setIsOpen(true);
226834
- },
226835
- // Rows that open on Enter/Space would otherwise swallow the key press.
226836
- onKeyDown: (event) => event.stopPropagation(), className: cn('max-w-full cursor-pointer truncate text-left hover:text-blue-600 hover:underline focus:text-blue-600 focus:outline-none', className), "aria-label": t('tabsUserstab.viewProfileAriaLabel', { name: display }), "data-testid": testId, children: display }), jsx(UserProfileDialog, { tenantKey: tenantKey, username: username, isOpen: isOpen, onClose: () => setIsOpen(false), customization: customization, currentSPA: currentSPA })] }));
226837
- }
226838
-
226839
228487
  /**
226840
228488
  * Add/edit dialog for one per-user spend limit, mirroring the grader
226841
228489
  * criterion modal: create mode starts with a searchable platform-user picker
@@ -229474,11 +231122,11 @@ function StripeConnect({ platformKey }) {
229474
231122
  }
229475
231123
  };
229476
231124
  if (isLoading) {
229477
- return (jsx(Card, { className: "shadow-sm border", style: { borderColor: 'oklch(.922 0 0)' }, children: jsx(CardContent, { className: "p-6", children: jsxs("div", { className: "flex items-center gap-4", children: [jsx(Skeleton, { className: "h-12 w-12 rounded-full" }), jsxs("div", { className: "flex-1 space-y-2", children: [jsx(Skeleton, { className: "h-5 w-48" }), jsx(Skeleton, { className: "h-4 w-72" })] }), jsx(Skeleton, { className: "h-9 w-32" })] }) }) }));
231125
+ return (jsx(Card$1, { className: "shadow-sm border", style: { borderColor: 'oklch(.922 0 0)' }, children: jsx(CardContent, { className: "p-6", children: jsxs("div", { className: "flex items-center gap-4", children: [jsx(Skeleton, { className: "h-12 w-12 rounded-full" }), jsxs("div", { className: "flex-1 space-y-2", children: [jsx(Skeleton, { className: "h-5 w-48" }), jsx(Skeleton, { className: "h-4 w-72" })] }), jsx(Skeleton, { className: "h-9 w-32" })] }) }) }));
229478
231126
  }
229479
231127
  const isReady = status === null || status === void 0 ? void 0 : status.is_ready_for_payments;
229480
231128
  const hasAccount = status === null || status === void 0 ? void 0 : status.has_account;
229481
- return (jsx(Card, { className: "shadow-sm border", style: { borderColor: 'oklch(.922 0 0)' }, children: jsx(CardContent, { className: "p-6", children: jsxs("div", { className: "flex flex-col sm:flex-row items-start sm:items-center gap-4", children: [jsxs("div", { className: "flex-1 min-w-0", children: [jsxs("div", { className: "flex items-center gap-2 mb-1", children: [jsx("h4", { className: "text-base font-semibold text-gray-900 dark:text-gray-100", children: t('monetizationStripeConnect.stripeConfiguration') }), isReady ? (jsx(Badge, { className: "bg-blue-100 text-blue-700 border-blue-200", children: t('monetizationStripeConnect.badgeConfigured') })) : hasAccount ? (jsx(Badge, { className: "bg-yellow-100 text-yellow-700 border-yellow-200", children: t('monetizationStripeConnect.badgeIncomplete') })) : (jsx(Badge, { variant: "secondary", children: t('monetizationStripeConnect.badgeNotConfigured') }))] }), isReady ? (jsxs("p", { className: "text-sm text-gray-500", children: [t('monetizationStripeConnect.stripeConnectedReady'), (status === null || status === void 0 ? void 0 : status.charges_enabled) && ' ' + t('monetizationStripeConnect.chargesEnabled'), (status === null || status === void 0 ? void 0 : status.payouts_enabled) && ' ' + t('monetizationStripeConnect.payoutsEnabled')] })) : hasAccount ? (jsx("p", { className: "text-sm text-gray-500", children: t('monetizationStripeConnect.onboardingIncomplete') })) : (jsx("p", { className: "text-sm text-gray-500", children: t('monetizationStripeConnect.connectStripePrompt') }))] }), jsx("div", { className: "flex gap-2 flex-shrink-0", children: isError ? null : isReady ? (jsxs(Button$1, { onClick: handleOpenDashboard, disabled: isRedirecting, variant: "outline", className: "cursor-pointer", children: [isRedirecting ? (jsx(LoaderCircle, { className: "h-4 w-4 mr-2 animate-spin" })) : (jsx(ExternalLink, { className: "h-4 w-4 mr-2" })), t('monetizationStripeConnect.stripeDashboardButton')] })) : (jsxs(Button$1, { onClick: handleStartOnboarding, disabled: isStartingOnboarding || isRedirecting, className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] hover:opacity-90 text-white cursor-pointer", children: [isStartingOnboarding || isRedirecting ? (jsx(LoaderCircle, { className: "h-4 w-4 mr-2 animate-spin" })) : (jsx(Link$1, { className: "h-4 w-4 mr-2" })), isStartingOnboarding || isRedirecting
231129
+ return (jsx(Card$1, { className: "shadow-sm border", style: { borderColor: 'oklch(.922 0 0)' }, children: jsx(CardContent, { className: "p-6", children: jsxs("div", { className: "flex flex-col sm:flex-row items-start sm:items-center gap-4", children: [jsxs("div", { className: "flex-1 min-w-0", children: [jsxs("div", { className: "flex items-center gap-2 mb-1", children: [jsx("h4", { className: "text-base font-semibold text-gray-900 dark:text-gray-100", children: t('monetizationStripeConnect.stripeConfiguration') }), isReady ? (jsx(Badge, { className: "bg-blue-100 text-blue-700 border-blue-200", children: t('monetizationStripeConnect.badgeConfigured') })) : hasAccount ? (jsx(Badge, { className: "bg-yellow-100 text-yellow-700 border-yellow-200", children: t('monetizationStripeConnect.badgeIncomplete') })) : (jsx(Badge, { variant: "secondary", children: t('monetizationStripeConnect.badgeNotConfigured') }))] }), isReady ? (jsxs("p", { className: "text-sm text-gray-500", children: [t('monetizationStripeConnect.stripeConnectedReady'), (status === null || status === void 0 ? void 0 : status.charges_enabled) && ' ' + t('monetizationStripeConnect.chargesEnabled'), (status === null || status === void 0 ? void 0 : status.payouts_enabled) && ' ' + t('monetizationStripeConnect.payoutsEnabled')] })) : hasAccount ? (jsx("p", { className: "text-sm text-gray-500", children: t('monetizationStripeConnect.onboardingIncomplete') })) : (jsx("p", { className: "text-sm text-gray-500", children: t('monetizationStripeConnect.connectStripePrompt') }))] }), jsx("div", { className: "flex gap-2 flex-shrink-0", children: isError ? null : isReady ? (jsxs(Button$1, { onClick: handleOpenDashboard, disabled: isRedirecting, variant: "outline", className: "cursor-pointer", children: [isRedirecting ? (jsx(LoaderCircle, { className: "h-4 w-4 mr-2 animate-spin" })) : (jsx(ExternalLink, { className: "h-4 w-4 mr-2" })), t('monetizationStripeConnect.stripeDashboardButton')] })) : (jsxs(Button$1, { onClick: handleStartOnboarding, disabled: isStartingOnboarding || isRedirecting, className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] hover:opacity-90 text-white cursor-pointer", children: [isStartingOnboarding || isRedirecting ? (jsx(LoaderCircle, { className: "h-4 w-4 mr-2 animate-spin" })) : (jsx(Link$1, { className: "h-4 w-4 mr-2" })), isStartingOnboarding || isRedirecting
229482
231130
  ? t('monetizationStripeConnect.redirecting')
229483
231131
  : hasAccount
229484
231132
  ? t('monetizationStripeConnect.completeSetup')
@@ -229631,12 +231279,12 @@ function PaywalledItemsList({ platformKey, onSelectItem, onAddCustomItem, }) {
229631
231279
  page_size: PAGE_SIZE$a,
229632
231280
  ...(statusFilter !== 'all' && { is_enabled: statusFilter === 'active' }),
229633
231281
  }, { refetchOnMountOrArgChange: true });
229634
- return (jsxs("div", { className: "space-y-4", children: [jsx(Card, { className: "shadow-sm border", style: { borderColor: 'oklch(.922 0 0)' }, children: jsxs(CardContent, { className: "p-4", children: [jsx("h4", { className: "text-sm font-medium text-gray-700 dark:text-gray-300 mb-3", children: t('monetizationPaywalledItemsList.configureItemForSale') }), jsxs("div", { className: "flex items-center gap-2", children: [jsx(ItemSearchDropdown, { platformKey: platformKey, onSelect: (itemType, uniqueId, name) => onSelectItem(itemType, uniqueId, name) }), jsx(Button$1, { variant: "outline", size: "icon", onClick: onAddCustomItem, className: "shrink-0 cursor-pointer", title: t('monetizationPaywalledItemsList.addCustomItemTitle'), children: jsx(Plus, { className: "h-4 w-4" }) })] })] }) }), jsxs("div", { className: "space-y-2", children: [jsxs("div", { className: "flex items-center justify-between", children: [jsx("h4", { className: "text-sm font-medium text-gray-500", children: t('monetizationPaywalledItemsList.configuredItems') }), jsxs(Select$1, { value: statusFilter, onValueChange: (v) => {
231282
+ return (jsxs("div", { className: "space-y-4", children: [jsx(Card$1, { className: "shadow-sm border", style: { borderColor: 'oklch(.922 0 0)' }, children: jsxs(CardContent, { className: "p-4", children: [jsx("h4", { className: "text-sm font-medium text-gray-700 dark:text-gray-300 mb-3", children: t('monetizationPaywalledItemsList.configureItemForSale') }), jsxs("div", { className: "flex items-center gap-2", children: [jsx(ItemSearchDropdown, { platformKey: platformKey, onSelect: (itemType, uniqueId, name) => onSelectItem(itemType, uniqueId, name) }), jsx(Button$1, { variant: "outline", size: "icon", onClick: onAddCustomItem, className: "shrink-0 cursor-pointer", title: t('monetizationPaywalledItemsList.addCustomItemTitle'), children: jsx(Plus, { className: "h-4 w-4" }) })] })] }) }), jsxs("div", { className: "space-y-2", children: [jsxs("div", { className: "flex items-center justify-between", children: [jsx("h4", { className: "text-sm font-medium text-gray-500", children: t('monetizationPaywalledItemsList.configuredItems') }), jsxs(Select$1, { value: statusFilter, onValueChange: (v) => {
229635
231283
  setStatusFilter(v);
229636
231284
  setPage(1);
229637
231285
  }, children: [jsx(SelectTrigger, { className: "w-[130px] h-8 text-xs", children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "all", children: t('monetizationPaywalledItemsList.filterAll') }), jsx(SelectItem, { value: "active", children: t('monetizationPaywalledItemsList.filterActive') }), jsx(SelectItem, { value: "disabled", children: t('monetizationPaywalledItemsList.filterDisabled') })] })] })] }), isLoading ? (jsx("div", { className: "space-y-2", children: [1, 2, 3].map((i) => (jsx(Skeleton, { className: "h-16 w-full" }, i))) })) : (paywalls === null || paywalls === void 0 ? void 0 : paywalls.results) && paywalls.results.length > 0 ? (jsxs(Fragment$1, { children: [paywalls.results.map((pw) => {
229638
231286
  var _a;
229639
- return (jsx(Card, { className: "shadow-sm border hover:border-blue-200 transition-colors cursor-pointer", style: { borderColor: 'oklch(.922 0 0)' }, onClick: () => onSelectItem(pw.item_type, pw.item_id, pw.item_name), children: jsxs(CardContent, { className: "p-4 flex items-center justify-between", children: [jsx("div", { className: "flex items-center gap-3", children: jsxs("div", { children: [jsx("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: pw.item_name || pw.item_id }), jsxs("div", { className: "flex items-center gap-2 mt-0.5", children: [jsx(Badge, { variant: "secondary", className: "text-xs capitalize", children: displayItemType(pw.item_type) }), ((_a = pw.prices) === null || _a === void 0 ? void 0 : _a.length) > 0 &&
231287
+ return (jsx(Card$1, { className: "shadow-sm border hover:border-blue-200 transition-colors cursor-pointer", style: { borderColor: 'oklch(.922 0 0)' }, onClick: () => onSelectItem(pw.item_type, pw.item_id, pw.item_name), children: jsxs(CardContent, { className: "p-4 flex items-center justify-between", children: [jsx("div", { className: "flex items-center gap-3", children: jsxs("div", { children: [jsx("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: pw.item_name || pw.item_id }), jsxs("div", { className: "flex items-center gap-2 mt-0.5", children: [jsx(Badge, { variant: "secondary", className: "text-xs capitalize", children: displayItemType(pw.item_type) }), ((_a = pw.prices) === null || _a === void 0 ? void 0 : _a.length) > 0 &&
229640
231288
  (() => {
229641
231289
  const activeCount = pw.prices.filter((p) => p.is_active).length;
229642
231290
  return activeCount > 0 ? (jsx("span", { className: "text-xs text-gray-400", children: activeCount !== 1
@@ -229692,7 +231340,7 @@ function PriceForm({ form, onChange, onSave, onCancel, isSaving, isEdit, }) {
229692
231340
  const handleRemoveFeature = (index) => {
229693
231341
  onChange({ ...form, features: form.features.filter((_, i) => i !== index) });
229694
231342
  };
229695
- return (jsx(Card, { className: "shadow-sm border border-blue-200 bg-blue-50/30", style: { borderColor: 'oklch(.922 0 0)' }, children: jsxs(CardContent, { className: "p-4 space-y-3", children: [jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-3", children: [jsxs("div", { children: [jsx(Label$1, { className: "text-xs text-gray-500", children: t('monetizationPriceManagement.nameLabel') }), jsx(Input, { value: form.name, onChange: (e) => onChange({ ...form, name: e.target.value }), placeholder: t('monetizationPriceManagement.namePlaceholder'), className: "mt-1" })] }), jsxs("div", { children: [jsx(Label$1, { className: "text-xs text-gray-500", children: t('monetizationPriceManagement.amountLabel') }), jsx(Input, { type: "number", min: 0, step: "0.01", value: form.amount, onChange: (e) => onChange({ ...form, amount: e.target.value }), placeholder: t('monetizationPriceManagement.amountPlaceholder'), className: "mt-1" })] }), jsxs("div", { children: [jsx(Label$1, { className: "text-xs text-gray-500", children: t('monetizationPriceManagement.intervalLabel') }), jsxs(Select$1, { value: form.interval, onValueChange: (v) => onChange({ ...form, interval: v }), children: [jsx(SelectTrigger, { className: "mt-1", children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "month", children: t('monetizationPriceManagement.intervalMonthly') }), jsx(SelectItem, { value: "year", children: t('monetizationPriceManagement.intervalYearly') }), jsx(SelectItem, { value: "one_time", children: t('monetizationPriceManagement.intervalOneTime') })] })] })] }), jsxs("div", { children: [jsx(Label$1, { className: "text-xs text-gray-500", children: t('monetizationPriceManagement.currencyLabel') }), jsx(Input, { value: form.currency, disabled: true, className: "mt-1 bg-gray-50 text-gray-500 cursor-not-allowed" })] })] }), jsxs("div", { children: [jsx(Label$1, { className: "text-xs text-gray-500", children: t('monetizationPriceManagement.descriptionLabel') }), jsx(Input, { value: form.description, onChange: (e) => onChange({ ...form, description: e.target.value }), placeholder: t('monetizationPriceManagement.descriptionPlaceholder'), className: "mt-1" })] }), jsxs("div", { children: [jsx(Label$1, { className: "text-xs text-gray-500", children: t('monetizationPriceManagement.featuresLabel') }), form.features.length > 0 && (jsx("div", { className: "flex flex-wrap gap-1.5 mt-1.5 mb-1.5", children: form.features.map((feature, i) => (jsxs(Badge, { variant: "secondary", className: "text-xs font-normal pl-2 pr-1 py-0.5 flex items-center gap-1", children: [feature, jsx("button", { type: "button", onClick: () => handleRemoveFeature(i), className: "hover:bg-gray-300 rounded-full p-0.5 transition-colors cursor-pointer", children: jsx(X$2, { className: "h-2.5 w-2.5" }) })] }, i))) })), jsxs("div", { className: "flex gap-1.5 mt-1", children: [jsx(Input, { value: featureInput, onChange: (e) => setFeatureInput(e.target.value), onKeyDown: (e) => {
231343
+ return (jsx(Card$1, { className: "shadow-sm border border-blue-200 bg-blue-50/30", style: { borderColor: 'oklch(.922 0 0)' }, children: jsxs(CardContent, { className: "p-4 space-y-3", children: [jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-3", children: [jsxs("div", { children: [jsx(Label$1, { className: "text-xs text-gray-500", children: t('monetizationPriceManagement.nameLabel') }), jsx(Input, { value: form.name, onChange: (e) => onChange({ ...form, name: e.target.value }), placeholder: t('monetizationPriceManagement.namePlaceholder'), className: "mt-1" })] }), jsxs("div", { children: [jsx(Label$1, { className: "text-xs text-gray-500", children: t('monetizationPriceManagement.amountLabel') }), jsx(Input, { type: "number", min: 0, step: "0.01", value: form.amount, onChange: (e) => onChange({ ...form, amount: e.target.value }), placeholder: t('monetizationPriceManagement.amountPlaceholder'), className: "mt-1" })] }), jsxs("div", { children: [jsx(Label$1, { className: "text-xs text-gray-500", children: t('monetizationPriceManagement.intervalLabel') }), jsxs(Select$1, { value: form.interval, onValueChange: (v) => onChange({ ...form, interval: v }), children: [jsx(SelectTrigger, { className: "mt-1", children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "month", children: t('monetizationPriceManagement.intervalMonthly') }), jsx(SelectItem, { value: "year", children: t('monetizationPriceManagement.intervalYearly') }), jsx(SelectItem, { value: "one_time", children: t('monetizationPriceManagement.intervalOneTime') })] })] })] }), jsxs("div", { children: [jsx(Label$1, { className: "text-xs text-gray-500", children: t('monetizationPriceManagement.currencyLabel') }), jsx(Input, { value: form.currency, disabled: true, className: "mt-1 bg-gray-50 text-gray-500 cursor-not-allowed" })] })] }), jsxs("div", { children: [jsx(Label$1, { className: "text-xs text-gray-500", children: t('monetizationPriceManagement.descriptionLabel') }), jsx(Input, { value: form.description, onChange: (e) => onChange({ ...form, description: e.target.value }), placeholder: t('monetizationPriceManagement.descriptionPlaceholder'), className: "mt-1" })] }), jsxs("div", { children: [jsx(Label$1, { className: "text-xs text-gray-500", children: t('monetizationPriceManagement.featuresLabel') }), form.features.length > 0 && (jsx("div", { className: "flex flex-wrap gap-1.5 mt-1.5 mb-1.5", children: form.features.map((feature, i) => (jsxs(Badge, { variant: "secondary", className: "text-xs font-normal pl-2 pr-1 py-0.5 flex items-center gap-1", children: [feature, jsx("button", { type: "button", onClick: () => handleRemoveFeature(i), className: "hover:bg-gray-300 rounded-full p-0.5 transition-colors cursor-pointer", children: jsx(X$2, { className: "h-2.5 w-2.5" }) })] }, i))) })), jsxs("div", { className: "flex gap-1.5 mt-1", children: [jsx(Input, { value: featureInput, onChange: (e) => setFeatureInput(e.target.value), onKeyDown: (e) => {
229696
231344
  if (e.key === 'Enter') {
229697
231345
  e.preventDefault();
229698
231346
  handleAddFeature();
@@ -229809,7 +231457,7 @@ function PriceManagement({ platformKey, itemType, itemId }) {
229809
231457
  return editingPriceId === price.id ? (jsx(PriceForm, { form: formData, onChange: setFormData, onSave: handleUpdate, onCancel: () => {
229810
231458
  setEditingPriceId(null);
229811
231459
  setFormData(EMPTY_FORM$1);
229812
- }, isSaving: isUpdating, isEdit: true }, price.id)) : (jsx(Card, { className: "shadow-sm border", style: { borderColor: 'oklch(.922 0 0)' }, children: jsx(CardContent, { className: "p-4", children: jsxs("div", { className: "flex items-center justify-between", children: [jsxs("div", { className: "flex items-center gap-3", children: [jsx(Tag, { className: "h-4 w-4 text-gray-400" }), jsxs("div", { children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: price.name }), jsxs("span", { className: "text-sm font-semibold text-blue-600", children: ["$", price.amount, " ", formatInterval(price.interval)] }), !price.is_active && (jsx(Badge, { variant: "secondary", className: "text-xs", children: t('monetizationPriceManagement.inactiveBadge') }))] }), price.description && (jsx("p", { className: "text-xs text-gray-400 mt-0.5", children: price.description })), ((_a = price.features) === null || _a === void 0 ? void 0 : _a.length) > 0 && (jsx("div", { className: "flex flex-wrap gap-1 mt-1", children: price.features.map((f, i) => (jsx(Badge, { variant: "outline", className: "text-xs font-normal", children: f }, i))) }))] })] }), jsxs("div", { className: "flex gap-1 flex-shrink-0", children: [jsx(Button$1, { onClick: () => handleStartEdit(price), variant: "ghost", size: "sm", className: "h-8 w-8 p-0 cursor-pointer", children: jsx(Pencil, { className: "h-3 w-3" }) }), jsx(Button$1, { onClick: () => handleDelete(price.id), variant: "ghost", size: "sm", disabled: deletingId === price.id, className: "h-8 w-8 p-0 text-red-500 hover:text-red-700 cursor-pointer", children: deletingId === price.id ? (jsx(LoaderCircle, { className: "h-3 w-3 animate-spin" })) : (jsx(Trash2, { className: "h-3 w-3" })) })] })] }) }) }, price.id));
231460
+ }, isSaving: isUpdating, isEdit: true }, price.id)) : (jsx(Card$1, { className: "shadow-sm border", style: { borderColor: 'oklch(.922 0 0)' }, children: jsx(CardContent, { className: "p-4", children: jsxs("div", { className: "flex items-center justify-between", children: [jsxs("div", { className: "flex items-center gap-3", children: [jsx(Tag, { className: "h-4 w-4 text-gray-400" }), jsxs("div", { children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: price.name }), jsxs("span", { className: "text-sm font-semibold text-blue-600", children: ["$", price.amount, " ", formatInterval(price.interval)] }), !price.is_active && (jsx(Badge, { variant: "secondary", className: "text-xs", children: t('monetizationPriceManagement.inactiveBadge') }))] }), price.description && (jsx("p", { className: "text-xs text-gray-400 mt-0.5", children: price.description })), ((_a = price.features) === null || _a === void 0 ? void 0 : _a.length) > 0 && (jsx("div", { className: "flex flex-wrap gap-1 mt-1", children: price.features.map((f, i) => (jsx(Badge, { variant: "outline", className: "text-xs font-normal", children: f }, i))) }))] })] }), jsxs("div", { className: "flex gap-1 flex-shrink-0", children: [jsx(Button$1, { onClick: () => handleStartEdit(price), variant: "ghost", size: "sm", className: "h-8 w-8 p-0 cursor-pointer", children: jsx(Pencil, { className: "h-3 w-3" }) }), jsx(Button$1, { onClick: () => handleDelete(price.id), variant: "ghost", size: "sm", disabled: deletingId === price.id, className: "h-8 w-8 p-0 text-red-500 hover:text-red-700 cursor-pointer", children: deletingId === price.id ? (jsx(LoaderCircle, { className: "h-3 w-3 animate-spin" })) : (jsx(Trash2, { className: "h-3 w-3" })) })] })] }) }) }, price.id));
229813
231461
  }) })) : (jsx("p", { className: "text-xs text-gray-400 text-center py-4", children: t('monetizationPriceManagement.emptyState') }))] }));
229814
231462
  }
229815
231463
 
@@ -229979,13 +231627,13 @@ function PaywallDetail({ platformKey, authURL, itemType, itemId, itemName, isCus
229979
231627
  const currentIndex = wizardSteps.findIndex((s) => s.key === currentStep);
229980
231628
  if (targetIndex < currentIndex)
229981
231629
  setCurrentStep(step);
229982
- } }), currentStep === 'custom-item' && isCustom && (jsx(Card, { className: "shadow-sm border", style: { borderColor: 'oklch(.922 0 0)' }, children: jsxs(CardContent, { className: "p-6 space-y-4", children: [jsx("h4", { className: "text-sm font-semibold text-gray-700 dark:text-gray-300", children: t('monetizationPaywallDetail.stepItemDetails') }), jsxs("div", { className: "space-y-3", children: [jsxs("div", { children: [jsx(Label$1, { className: "text-sm font-medium", children: t('monetizationPaywallDetail.labelItemType') }), jsx(Input, { value: customItemType, onChange: (e) => setCustomItemType(e.target.value), placeholder: t('monetizationPaywallDetail.placeholderItemType'), className: "mt-1", disabled: itemCreated }), customItemType && (jsx("p", { className: "text-xs text-gray-400 mt-1", children: t('monetizationPaywallDetail.slugPreview', { slug: slugify(customItemType) }) }))] }), jsxs("div", { children: [jsx(Label$1, { className: "text-sm font-medium", children: t('monetizationPaywallDetail.labelItemName') }), jsx(Input, { value: customItemName, onChange: (e) => setCustomItemName(e.target.value), placeholder: t('monetizationPaywallDetail.placeholderItemName'), className: "mt-1", disabled: itemCreated }), customItemName && (jsx("p", { className: "text-xs text-gray-400 mt-1", children: t('monetizationPaywallDetail.idPreview', { id: slugify(customItemName) }) }))] }), jsxs("div", { children: [jsx(Label$1, { className: "text-sm font-medium", children: t('monetizationPaywallDetail.labelDescription') }), jsx(Textarea, { value: customDescription, onChange: (e) => setCustomDescription(e.target.value), placeholder: t('monetizationPaywallDetail.placeholderDescription'), className: "mt-1", rows: 3 })] }), jsxs("div", { children: [jsx(Label$1, { className: "text-sm font-medium", children: t('monetizationPaywallDetail.labelProductUrl') }), jsx(Input, { type: "url", value: customProductUrl, onChange: (e) => setCustomProductUrl(e.target.value), placeholder: t('monetizationPaywallDetail.placeholderProductUrl'), className: "mt-1", disabled: itemCreated }), jsx("p", { className: "text-xs text-gray-400 mt-1", children: t('monetizationPaywallDetail.hintProductUrl') })] })] }), jsx("div", { className: "pt-3 border-t flex justify-end", children: jsxs(Button$1, { onClick: handleCreateCustomItem, disabled: isSaving || !customItemType.trim() || !customItemName.trim(), className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] hover:opacity-90 text-white cursor-pointer", children: [isCreating ? (jsx(LoaderCircle, { className: "h-4 w-4 mr-2 animate-spin" })) : (jsx(ChevronRight, { className: "h-4 w-4 mr-2" })), t('monetizationPaywallDetail.createAndContinue')] }) })] }) })), currentStep === 'paywall' && (jsx(Card, { className: "shadow-sm border", style: { borderColor: 'oklch(.922 0 0)' }, children: jsxs(CardContent, { className: "p-6 space-y-5", children: [jsxs("div", { className: "flex items-center justify-between", children: [jsxs("div", { children: [jsx(Label$1, { className: "text-sm font-medium", children: t('monetizationPaywallDetail.labelPaywallEnabled') }), jsx("p", { className: "text-xs text-gray-400 mt-0.5", children: t('monetizationPaywallDetail.hintPaywallEnabled') })] }), jsx(Switch, { checked: isEnabled, onCheckedChange: setIsEnabled, className: "cursor-pointer data-[state=checked]:bg-blue-500" })] }), jsxs("div", { className: !isEnabled ? 'opacity-50 pointer-events-none' : '', children: [jsxs("div", { className: "flex items-center justify-between py-3 border-t", children: [jsxs("div", { children: [jsx(Label$1, { className: "text-sm font-medium", children: t('monetizationPaywallDetail.labelAllowFreeTier') }), jsx("p", { className: "text-xs text-gray-400 mt-0.5", children: t('monetizationPaywallDetail.hintAllowFreeTier') })] }), jsx(Switch, { checked: allowFreeTier, onCheckedChange: setAllowFreeTier, className: "cursor-pointer data-[state=checked]:bg-blue-500" })] }), jsxs("div", { className: "flex items-center justify-between py-3 border-t", children: [jsxs("div", { children: [jsx(Label$1, { className: "text-sm font-medium", children: t('monetizationPaywallDetail.labelTrialPeriod') }), jsx("p", { className: "text-xs text-gray-400 mt-0.5", children: t('monetizationPaywallDetail.hintTrialPeriod') })] }), jsx(Input, { type: "number", min: 0, value: trialDays, onChange: (e) => setTrialDays(parseInt(e.target.value) || 0), className: "w-24 text-right" })] }), jsxs("div", { className: "flex items-center justify-between py-3 border-t", children: [jsxs("div", { children: [jsx(Label$1, { className: "text-sm font-medium", children: t('monetizationPaywallDetail.labelExistingUsers') }), jsx("p", { className: "text-xs text-gray-400 mt-0.5", children: t('monetizationPaywallDetail.hintExistingUsers') })] }), jsxs(Select$1, { value: grandfathering, onValueChange: (v) => setGrandfathering(v), children: [jsx(SelectTrigger, { className: "w-[180px]", children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "free_forever", children: t('monetizationPaywallDetail.grandfatheringFreeForever') }), jsx(SelectItem, { value: "require_subscription", children: t('monetizationPaywallDetail.grandfatheringRequireSubscription') })] })] })] })] }), jsx("div", { className: "pt-3 border-t flex justify-end", children: jsxs(Button$1, { onClick: handleSaveConfig, disabled: isSaving, className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] hover:opacity-90 text-white cursor-pointer", children: [isSaving ? (jsx(LoaderCircle, { className: "h-4 w-4 mr-2 animate-spin" })) : (jsx(Check, { className: "h-4 w-4 mr-2" })), t('monetizationPaywallDetail.saveConfiguration')] }) })] }) })), currentStep === 'pricing' && (jsx(PriceManagement, { platformKey: platformKey, itemType: resolvedItemType, itemId: resolvedItemId }))] }));
231630
+ } }), currentStep === 'custom-item' && isCustom && (jsx(Card$1, { className: "shadow-sm border", style: { borderColor: 'oklch(.922 0 0)' }, children: jsxs(CardContent, { className: "p-6 space-y-4", children: [jsx("h4", { className: "text-sm font-semibold text-gray-700 dark:text-gray-300", children: t('monetizationPaywallDetail.stepItemDetails') }), jsxs("div", { className: "space-y-3", children: [jsxs("div", { children: [jsx(Label$1, { className: "text-sm font-medium", children: t('monetizationPaywallDetail.labelItemType') }), jsx(Input, { value: customItemType, onChange: (e) => setCustomItemType(e.target.value), placeholder: t('monetizationPaywallDetail.placeholderItemType'), className: "mt-1", disabled: itemCreated }), customItemType && (jsx("p", { className: "text-xs text-gray-400 mt-1", children: t('monetizationPaywallDetail.slugPreview', { slug: slugify(customItemType) }) }))] }), jsxs("div", { children: [jsx(Label$1, { className: "text-sm font-medium", children: t('monetizationPaywallDetail.labelItemName') }), jsx(Input, { value: customItemName, onChange: (e) => setCustomItemName(e.target.value), placeholder: t('monetizationPaywallDetail.placeholderItemName'), className: "mt-1", disabled: itemCreated }), customItemName && (jsx("p", { className: "text-xs text-gray-400 mt-1", children: t('monetizationPaywallDetail.idPreview', { id: slugify(customItemName) }) }))] }), jsxs("div", { children: [jsx(Label$1, { className: "text-sm font-medium", children: t('monetizationPaywallDetail.labelDescription') }), jsx(Textarea, { value: customDescription, onChange: (e) => setCustomDescription(e.target.value), placeholder: t('monetizationPaywallDetail.placeholderDescription'), className: "mt-1", rows: 3 })] }), jsxs("div", { children: [jsx(Label$1, { className: "text-sm font-medium", children: t('monetizationPaywallDetail.labelProductUrl') }), jsx(Input, { type: "url", value: customProductUrl, onChange: (e) => setCustomProductUrl(e.target.value), placeholder: t('monetizationPaywallDetail.placeholderProductUrl'), className: "mt-1", disabled: itemCreated }), jsx("p", { className: "text-xs text-gray-400 mt-1", children: t('monetizationPaywallDetail.hintProductUrl') })] })] }), jsx("div", { className: "pt-3 border-t flex justify-end", children: jsxs(Button$1, { onClick: handleCreateCustomItem, disabled: isSaving || !customItemType.trim() || !customItemName.trim(), className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] hover:opacity-90 text-white cursor-pointer", children: [isCreating ? (jsx(LoaderCircle, { className: "h-4 w-4 mr-2 animate-spin" })) : (jsx(ChevronRight, { className: "h-4 w-4 mr-2" })), t('monetizationPaywallDetail.createAndContinue')] }) })] }) })), currentStep === 'paywall' && (jsx(Card$1, { className: "shadow-sm border", style: { borderColor: 'oklch(.922 0 0)' }, children: jsxs(CardContent, { className: "p-6 space-y-5", children: [jsxs("div", { className: "flex items-center justify-between", children: [jsxs("div", { children: [jsx(Label$1, { className: "text-sm font-medium", children: t('monetizationPaywallDetail.labelPaywallEnabled') }), jsx("p", { className: "text-xs text-gray-400 mt-0.5", children: t('monetizationPaywallDetail.hintPaywallEnabled') })] }), jsx(Switch, { checked: isEnabled, onCheckedChange: setIsEnabled, className: "cursor-pointer data-[state=checked]:bg-blue-500" })] }), jsxs("div", { className: !isEnabled ? 'opacity-50 pointer-events-none' : '', children: [jsxs("div", { className: "flex items-center justify-between py-3 border-t", children: [jsxs("div", { children: [jsx(Label$1, { className: "text-sm font-medium", children: t('monetizationPaywallDetail.labelAllowFreeTier') }), jsx("p", { className: "text-xs text-gray-400 mt-0.5", children: t('monetizationPaywallDetail.hintAllowFreeTier') })] }), jsx(Switch, { checked: allowFreeTier, onCheckedChange: setAllowFreeTier, className: "cursor-pointer data-[state=checked]:bg-blue-500" })] }), jsxs("div", { className: "flex items-center justify-between py-3 border-t", children: [jsxs("div", { children: [jsx(Label$1, { className: "text-sm font-medium", children: t('monetizationPaywallDetail.labelTrialPeriod') }), jsx("p", { className: "text-xs text-gray-400 mt-0.5", children: t('monetizationPaywallDetail.hintTrialPeriod') })] }), jsx(Input, { type: "number", min: 0, value: trialDays, onChange: (e) => setTrialDays(parseInt(e.target.value) || 0), className: "w-24 text-right" })] }), jsxs("div", { className: "flex items-center justify-between py-3 border-t", children: [jsxs("div", { children: [jsx(Label$1, { className: "text-sm font-medium", children: t('monetizationPaywallDetail.labelExistingUsers') }), jsx("p", { className: "text-xs text-gray-400 mt-0.5", children: t('monetizationPaywallDetail.hintExistingUsers') })] }), jsxs(Select$1, { value: grandfathering, onValueChange: (v) => setGrandfathering(v), children: [jsx(SelectTrigger, { className: "w-[180px]", children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "free_forever", children: t('monetizationPaywallDetail.grandfatheringFreeForever') }), jsx(SelectItem, { value: "require_subscription", children: t('monetizationPaywallDetail.grandfatheringRequireSubscription') })] })] })] })] }), jsx("div", { className: "pt-3 border-t flex justify-end", children: jsxs(Button$1, { onClick: handleSaveConfig, disabled: isSaving, className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] hover:opacity-90 text-white cursor-pointer", children: [isSaving ? (jsx(LoaderCircle, { className: "h-4 w-4 mr-2 animate-spin" })) : (jsx(Check, { className: "h-4 w-4 mr-2" })), t('monetizationPaywallDetail.saveConfiguration')] }) })] }) })), currentStep === 'pricing' && (jsx(PriceManagement, { platformKey: platformKey, itemType: resolvedItemType, itemId: resolvedItemId }))] }));
229983
231631
  }
229984
231632
 
229985
231633
  function PaywallConfig({ platformKey, authURL, disabled }) {
229986
231634
  const [selectedItem, setSelectedItem] = useState(null);
229987
231635
  if (disabled) {
229988
- return (jsx(Card, { className: "shadow-sm border border-dashed", style: { borderColor: 'oklch(.922 0 0)' }, children: jsxs(CardContent, { className: "p-8 text-center", children: [jsx(Shield, { className: "h-10 w-10 mx-auto text-gray-300 mb-3" }), jsx("p", { className: "text-sm text-gray-400", children: "Connect Stripe first to configure paywalls." })] }) }));
231636
+ return (jsx(Card$1, { className: "shadow-sm border border-dashed", style: { borderColor: 'oklch(.922 0 0)' }, children: jsxs(CardContent, { className: "p-8 text-center", children: [jsx(Shield, { className: "h-10 w-10 mx-auto text-gray-300 mb-3" }), jsx("p", { className: "text-sm text-gray-400", children: "Connect Stripe first to configure paywalls." })] }) }));
229989
231637
  }
229990
231638
  if (selectedItem) {
229991
231639
  return (jsx(PaywallDetail, { platformKey: platformKey, authURL: authURL, itemType: selectedItem.type, itemId: selectedItem.id, itemName: selectedItem.name, isCustom: selectedItem.isCustom, onBack: () => setSelectedItem(null) }));
@@ -247096,7 +248744,7 @@ function inputRulesPlugin(props) {
247096
248744
  function getType(value) {
247097
248745
  return Object.prototype.toString.call(value).slice(8, -1);
247098
248746
  }
247099
- function isPlainObject(value) {
248747
+ function isPlainObject$1(value) {
247100
248748
  if (getType(value) !== "Object") {
247101
248749
  return false;
247102
248750
  }
@@ -247106,9 +248754,9 @@ function isPlainObject(value) {
247106
248754
  // src/utilities/mergeDeep.ts
247107
248755
  function mergeDeep(target, source) {
247108
248756
  const output = { ...target };
247109
- if (isPlainObject(target) && isPlainObject(source)) {
248757
+ if (isPlainObject$1(target) && isPlainObject$1(source)) {
247110
248758
  Object.keys(source).forEach((key) => {
247111
- if (isPlainObject(source[key]) && isPlainObject(target[key])) {
248759
+ if (isPlainObject$1(source[key]) && isPlainObject$1(target[key])) {
247112
248760
  output[key] = mergeDeep(target[key], source[key]);
247113
248761
  } else {
247114
248762
  output[key] = source[key];
@@ -258125,6 +259773,492 @@ AnalyticsScopeContext.Provider;
258125
259773
 
258126
259774
  Array.from({ length: 24 }, (_, i) => i);
258127
259775
 
259776
+ const asArray = (value) => (Array.isArray(value) ? value : []);
259777
+ const hasEntries = (value) => !!value && typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length > 0;
259778
+ /** Rollup counts as the conversation-list endpoint reports them: numbers, numeric strings or `null`. */
259779
+ const toRollupCount = (value) => {
259780
+ const count = typeof value === 'string' ? Number(value) : (value !== null && value !== void 0 ? value : 0);
259781
+ return Number.isFinite(count) && count > 0 ? Math.floor(count) : 0;
259782
+ };
259783
+ /**
259784
+ * Sum the retrieved documents and tool calls over a conversation's turns, for
259785
+ * surfaces (the admin chat-history list) that carry the turns inline but have
259786
+ * no server-side rollup fields.
259787
+ */
259788
+ function summarizeTranscriptTurns(turns) {
259789
+ return asArray(turns).reduce((rollup, turn) => ({
259790
+ documentsCount: rollup.documentsCount + asArray(turn === null || turn === void 0 ? void 0 : turn.documents).length,
259791
+ toolCallsCount: rollup.toolCallsCount + asArray(turn === null || turn === void 0 ? void 0 : turn.tool_calls).length,
259792
+ }), { documentsCount: 0, toolCallsCount: 0 });
259793
+ }
259794
+ /** A transcript's retrieved documents in the shape the chat's documents dialog lists. */
259795
+ function toRetrievedDocuments(documents) {
259796
+ return documents.map((doc) => {
259797
+ var _a, _b, _c, _d;
259798
+ return ({
259799
+ title: (_b = (_a = doc.title) !== null && _a !== void 0 ? _a : doc.source) !== null && _b !== void 0 ? _b : null,
259800
+ snippet: (_c = doc.snippet) !== null && _c !== void 0 ? _c : null,
259801
+ source: (_d = doc.source) !== null && _d !== void 0 ? _d : null,
259802
+ score: typeof doc.score === 'number' ? doc.score : null,
259803
+ });
259804
+ });
259805
+ }
259806
+ /**
259807
+ * Every document a conversation retrieved, across its turns, exactly as the
259808
+ * backend reports them — a source retrieved again on a later turn appears
259809
+ * again, so the count matches the backend's per-conversation totals. Comes
259810
+ * from the admin history payload itself, so it needs no per-user session
259811
+ * lookup.
259812
+ */
259813
+ function conversationDocuments(turns) {
259814
+ return toRetrievedDocuments(asArray(turns).flatMap((turn) => asArray(turn === null || turn === void 0 ? void 0 : turn.documents)));
259815
+ }
259816
+ const asText = (value) => {
259817
+ if (value === null || value === undefined)
259818
+ return '';
259819
+ if (typeof value === 'string')
259820
+ return value;
259821
+ if (typeof value === 'number' || typeof value === 'boolean')
259822
+ return String(value);
259823
+ try {
259824
+ return JSON.stringify(value, null, 2);
259825
+ }
259826
+ catch (_a) {
259827
+ return String(value);
259828
+ }
259829
+ };
259830
+ /** A transcript's tool calls in the shape the chat's "Used N tools" record renders. */
259831
+ function toToolCallInfos(toolCalls) {
259832
+ return toolCalls.map((call, index) => {
259833
+ var _a;
259834
+ return ({
259835
+ id: `transcript-tool-${index}`,
259836
+ name: (_a = call.name) !== null && _a !== void 0 ? _a : '',
259837
+ input: call.input && typeof call.input === 'object' && !Array.isArray(call.input)
259838
+ ? call.input
259839
+ : typeof call.input === 'string'
259840
+ ? { input: call.input }
259841
+ : undefined,
259842
+ log: '',
259843
+ result: asText(call.output),
259844
+ });
259845
+ });
259846
+ }
259847
+ /** Whether a turn carries anything worth a details panel. */
259848
+ function hasTranscriptTurnDetails(turn) {
259849
+ if (!turn)
259850
+ return false;
259851
+ return (asArray(turn.documents).length > 0 ||
259852
+ asArray(turn.tool_calls).length > 0 ||
259853
+ hasEntries(turn.metadata) ||
259854
+ hasEntries(turn.request_context));
259855
+ }
259856
+ const isPlainObject = (value) => !!value && typeof value === 'object' && !Array.isArray(value);
259857
+ /** Nothing worth a row: null, blank strings, empty arrays and empty objects. */
259858
+ const isPresent = (value) => {
259859
+ if (value === null || value === undefined)
259860
+ return false;
259861
+ if (typeof value === 'string')
259862
+ return value.trim() !== '';
259863
+ if (Array.isArray(value))
259864
+ return value.length > 0;
259865
+ if (isPlainObject(value))
259866
+ return Object.keys(value).length > 0;
259867
+ return true;
259868
+ };
259869
+ const FIELD_ABBREVIATIONS = new Set([
259870
+ 'ai',
259871
+ 'api',
259872
+ 'css',
259873
+ 'csv',
259874
+ 'html',
259875
+ 'http',
259876
+ 'https',
259877
+ 'id',
259878
+ 'ids',
259879
+ 'ip',
259880
+ 'json',
259881
+ 'llm',
259882
+ 'lti',
259883
+ 'mcp',
259884
+ 'mime',
259885
+ 'pdf',
259886
+ 'rag',
259887
+ 'sso',
259888
+ 'stt',
259889
+ 'tts',
259890
+ 'ui',
259891
+ 'uri',
259892
+ 'url',
259893
+ 'urls',
259894
+ 'uuid',
259895
+ ]);
259896
+ /**
259897
+ * "file_references" → "File References", "llmModel" → "LLM Model". Backend
259898
+ * keys are never shown raw: every unknown key goes through this, and the
259899
+ * common ones get a translated label on top (see `useFieldLabels`).
259900
+ */
259901
+ function humanizeFieldName(key) {
259902
+ const words = key
259903
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
259904
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
259905
+ .toLowerCase()
259906
+ .split(/[^a-z0-9]+/)
259907
+ .filter(Boolean);
259908
+ if (words.length === 0)
259909
+ return key;
259910
+ return words
259911
+ .map((word) => FIELD_ABBREVIATIONS.has(word)
259912
+ ? word.toUpperCase()
259913
+ : word.charAt(0).toUpperCase() + word.slice(1))
259914
+ .join(' ');
259915
+ }
259916
+ /** Translated labels for the keys the backend is known to send. */
259917
+ function useFieldLabels() {
259918
+ const t = useT();
259919
+ const known = {
259920
+ llm_model: t('analyticsTranscriptTurnDetails.model'),
259921
+ llm_provider: t('analyticsTranscriptTurnDetails.provider'),
259922
+ llm_temperature: t('analyticsTranscriptTurnDetails.temperature'),
259923
+ model: t('analyticsTranscriptTurnDetails.model'),
259924
+ provider: t('analyticsTranscriptTurnDetails.provider'),
259925
+ temperature: t('analyticsTranscriptTurnDetails.temperature'),
259926
+ client: t('analyticsTranscriptTurnDetails.client'),
259927
+ session_id: t('analyticsTranscriptTurnDetails.fieldSession'),
259928
+ // The product's own names: artifacts are "Canvas" in the chat, proactivity
259929
+ // is the "Proactive Prompt", grading is the "Grader" tab, document filters
259930
+ // are "Datasets".
259931
+ enable_artifacts: t('analyticsTranscriptTurnDetails.fieldCanvas'),
259932
+ artifact: t('analyticsTranscriptTurnDetails.fieldCanvas'),
259933
+ artifacts: t('analyticsTranscriptTurnDetails.fieldCanvas'),
259934
+ is_proactive: t('analyticsTranscriptTurnDetails.fieldProactivePrompt'),
259935
+ grading_context: t('analyticsTranscriptTurnDetails.fieldGrader'),
259936
+ document_filter: t('analyticsTranscriptTurnDetails.fieldDatasets'),
259937
+ current_datetime: t('analyticsTranscriptTurnDetails.fieldDateTime'),
259938
+ caller_verified: t('analyticsTranscriptTurnDetails.fieldCallerVerified'),
259939
+ file_references: t('analyticsTranscriptTurnDetails.fieldFiles'),
259940
+ file_id: t('analyticsTranscriptTurnDetails.fieldFile'),
259941
+ system_prompt: t('analyticsTranscriptTurnDetails.fieldSystemPrompt'),
259942
+ flow: t('analyticsTranscriptTurnDetails.fieldFlow'),
259943
+ token: t('analyticsTranscriptTurnDetails.fieldToken'),
259944
+ tenant: t('analyticsTranscriptTurnDetails.fieldTenant'),
259945
+ pathway: t('analyticsTranscriptTurnDetails.fieldPathway'),
259946
+ username: t('analyticsTranscriptTurnDetails.fieldUsername'),
259947
+ name: t('analyticsTranscriptTurnDetails.fieldName'),
259948
+ };
259949
+ return (key) => { var _a; return (_a = known[key]) !== null && _a !== void 0 ? _a : humanizeFieldName(key); };
259950
+ }
259951
+ const fileName = (value) => {
259952
+ var _a, _b, _c, _d;
259953
+ const name = (_b = (_a = value.file_name) !== null && _a !== void 0 ? _a : value.filename) !== null && _b !== void 0 ? _b : value.name;
259954
+ if (typeof name === 'string' && name.trim())
259955
+ return name.trim();
259956
+ const key = (_d = (_c = value.file_key) !== null && _c !== void 0 ? _c : value.key) !== null && _d !== void 0 ? _d : value.path;
259957
+ if (typeof key === 'string' && key.trim())
259958
+ return key.trim().split('/').pop() || key.trim();
259959
+ return undefined;
259960
+ };
259961
+ /** Backend payloads name files in a few shapes; anything with a name plus a file-ish fact counts. */
259962
+ function isFileReference(value) {
259963
+ if (!isPlainObject(value) || !fileName(value))
259964
+ return false;
259965
+ return ['file_id', 'file_key', 'file_size', 'content_type', 'mime_type', 'url', 'size'].some((key) => key in value && value[key] !== null && value[key] !== undefined);
259966
+ }
259967
+ const attachmentUrl = (name, attachments) => { var _a; return (_a = attachments === null || attachments === void 0 ? void 0 : attachments.find((file) => (file === null || file === void 0 ? void 0 : file.name) === name && !!file.url)) === null || _a === void 0 ? void 0 : _a.url; };
259968
+ /** Normalise a file reference; the turn's attachments lend their presigned `url` to a same-named file. */
259969
+ function toFileReference(value, attachments) {
259970
+ var _a, _b, _c, _d, _e, _f;
259971
+ const name = (_a = fileName(value)) !== null && _a !== void 0 ? _a : '';
259972
+ const contentType = (_c = (_b = value.content_type) !== null && _b !== void 0 ? _b : value.mime_type) !== null && _c !== void 0 ? _c : value.type;
259973
+ const size = (_d = value.file_size) !== null && _d !== void 0 ? _d : value.size;
259974
+ const own = (_f = (_e = value.url) !== null && _e !== void 0 ? _e : value.download_url) !== null && _f !== void 0 ? _f : value.href;
259975
+ const numericSize = typeof size === 'number'
259976
+ ? size
259977
+ : typeof size === 'string' && size.trim() && Number.isFinite(Number(size))
259978
+ ? Number(size)
259979
+ : undefined;
259980
+ return {
259981
+ name,
259982
+ contentType: typeof contentType === 'string' ? contentType : undefined,
259983
+ size: numericSize,
259984
+ url: typeof own === 'string' && own ? own : attachmentUrl(name, attachments),
259985
+ };
259986
+ }
259987
+ const REDACTED_PATTERN = /^\[?(redacted|hidden|masked|removed)\]?$|^\*{3,}$/i;
259988
+ const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
259989
+ /** Past this depth nested objects flatten to "key: value" text rather than nesting further. */
259990
+ const MAX_NESTING = 4;
259991
+ const formatPrimitive = (value) => {
259992
+ if (value === null || value === undefined)
259993
+ return '';
259994
+ if (typeof value === 'string')
259995
+ return value;
259996
+ if (typeof value === 'number')
259997
+ return Number.isInteger(value) ? String(value) : String(value);
259998
+ return String(value);
259999
+ };
260000
+ /** Deeply nested leftovers as readable "Key: value" text — never JSON. */
260001
+ function compactText(value, labelFor) {
260002
+ if (Array.isArray(value)) {
260003
+ return value
260004
+ .filter(isPresent)
260005
+ .map((item) => compactText(item, labelFor))
260006
+ .join('; ');
260007
+ }
260008
+ if (isPlainObject(value)) {
260009
+ return Object.entries(value)
260010
+ .filter(([, v]) => isPresent(v))
260011
+ .map(([k, v]) => `${labelFor(k)}: ${compactText(v, labelFor)}`)
260012
+ .join(', ');
260013
+ }
260014
+ return formatPrimitive(value);
260015
+ }
260016
+ function BooleanValue({ value }) {
260017
+ const t = useT();
260018
+ return (jsx("span", { className: cn('inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium', value ? 'bg-green-50 text-green-700' : 'bg-gray-100 text-gray-600'), children: value ? t('analyticsTranscriptTurnDetails.yes') : t('analyticsTranscriptTurnDetails.no') }));
260019
+ }
260020
+ function StringValue({ value }) {
260021
+ const t = useT();
260022
+ const text = value.trim();
260023
+ if (REDACTED_PATTERN.test(text)) {
260024
+ return (jsx("span", { className: "text-gray-400 italic", children: t('analyticsTranscriptTurnDetails.redacted') }));
260025
+ }
260026
+ if (UUID_PATTERN.test(text)) {
260027
+ return jsx("span", { className: "font-mono text-xs break-all text-gray-700", children: text });
260028
+ }
260029
+ return (jsx("span", { className: "block max-h-48 overflow-y-auto break-words whitespace-pre-line", children: text }));
260030
+ }
260031
+ const DetailCtx = React__default.createContext({ labelFor: humanizeFieldName });
260032
+ function DetailValue({ value, depth, labelFor, }) {
260033
+ var _a, _b;
260034
+ const ctx = React__default.useContext(DetailCtx);
260035
+ if (typeof value === 'boolean')
260036
+ return jsx(BooleanValue, { value: value });
260037
+ if (typeof value === 'number')
260038
+ return jsx("span", { className: "tabular-nums", children: formatPrimitive(value) });
260039
+ if (typeof value === 'string')
260040
+ return jsx(StringValue, { value: value });
260041
+ if (depth >= MAX_NESTING) {
260042
+ return jsx("span", { className: "break-words", children: compactText(value, labelFor) });
260043
+ }
260044
+ if (isFileReference(value)) {
260045
+ const files = [toFileReference(value, ctx.attachments)];
260046
+ return jsx(Fragment$1, { children: ((_a = ctx.renderFiles) !== null && _a !== void 0 ? _a : defaultRenderFiles)(files) });
260047
+ }
260048
+ if (Array.isArray(value)) {
260049
+ const items = value.filter(isPresent);
260050
+ if (items.length === 0)
260051
+ return null;
260052
+ if (items.every(isFileReference)) {
260053
+ const files = items.map((item) => toFileReference(item, ctx.attachments));
260054
+ return jsx(Fragment$1, { children: ((_b = ctx.renderFiles) !== null && _b !== void 0 ? _b : defaultRenderFiles)(files) });
260055
+ }
260056
+ const allPrimitive = items.every((item) => !isPlainObject(item) && !Array.isArray(item));
260057
+ if (allPrimitive) {
260058
+ return (jsx("span", { className: "flex flex-wrap gap-1", children: items.map((item, index) => (jsx(Badge, { variant: "secondary", className: "max-w-full font-normal break-all whitespace-normal", children: typeof item === 'boolean' ? jsx(BooleanValue, { value: item }) : formatPrimitive(item) }, index))) }));
260059
+ }
260060
+ return (jsx("ol", { className: "space-y-2", children: items.map((item, index) => (jsxs("li", { className: "flex gap-2", children: [jsxs("span", { className: "mt-1 text-xs font-medium text-gray-400 tabular-nums", children: [index + 1, "."] }), jsx("div", { className: "min-w-0 flex-1 rounded-md border border-gray-200 px-3 py-1", children: isPlainObject(item) ? (jsx(DetailRows, { entries: item, depth: depth + 1, labelFor: labelFor })) : (jsx(DetailValue, { value: item, depth: depth + 1, labelFor: labelFor })) })] }, index))) }));
260061
+ }
260062
+ if (isPlainObject(value)) {
260063
+ return (jsx("div", { className: "border-l-2 border-gray-200 pl-3", children: jsx(DetailRows, { entries: value, depth: depth + 1, labelFor: labelFor }) }));
260064
+ }
260065
+ return jsx("span", { children: formatPrimitive(value) });
260066
+ }
260067
+ /**
260068
+ * Label/value rows for a metadata-style object. Keys are humanized (never
260069
+ * shown raw), blanks are skipped, booleans read Yes/No, lists become chips or
260070
+ * numbered cards, files become the chat's file cards and nested objects
260071
+ * indent — the same read-only detail style as the audit-log sheet, so nothing
260072
+ * here ever looks like a JSON dump.
260073
+ */
260074
+ function DetailRows({ entries, depth = 0, labelFor, }) {
260075
+ const rows = Object.entries(entries).filter(([, value]) => isPresent(value));
260076
+ if (rows.length === 0)
260077
+ return null;
260078
+ const top = depth === 0;
260079
+ return (jsx("dl", { className: cn(top ? 'divide-y divide-gray-100' : 'space-y-1'), children: rows.map(([key, value]) => (jsxs("div", { className: cn('grid gap-x-3', top
260080
+ ? 'grid-cols-[minmax(0,7rem)_minmax(0,1fr)] py-2'
260081
+ : 'grid-cols-[minmax(0,6rem)_minmax(0,1fr)] py-0.5'), children: [jsx("dt", { className: cn('break-words text-gray-500', top ? 'text-xs font-medium tracking-wide uppercase' : 'text-xs font-medium'), children: labelFor(key) }), jsx("dd", { className: "min-w-0 text-sm text-gray-800", children: jsx(DetailValue, { value: value, depth: depth, labelFor: labelFor }) })] }, key))) }));
260082
+ }
260083
+ function SectionHeading({ icon, children }) {
260084
+ return (jsxs("h4", { className: "mb-2 flex items-center gap-1.5 text-xs font-semibold tracking-wide text-gray-700 uppercase", children: [icon, children] }));
260085
+ }
260086
+ const MODEL_KEYS = new Set(['llm_model', 'llm_provider']);
260087
+ function MetadataSection({ metadata, renderModel, }) {
260088
+ var _a, _b;
260089
+ const t = useT();
260090
+ const labelFor = useFieldLabels();
260091
+ const rest = Object.fromEntries(Object.entries(metadata).filter(([key]) => !MODEL_KEYS.has(key)));
260092
+ const model = ((_a = metadata.llm_model) !== null && _a !== void 0 ? _a : '').trim() || undefined;
260093
+ const provider = ((_b = metadata.llm_provider) !== null && _b !== void 0 ? _b : '').trim() || undefined;
260094
+ const hasModel = !!(model || provider);
260095
+ return (jsxs("section", { "data-testid": "transcript-turn-metadata", children: [jsx(SectionHeading, { icon: jsx(Info$3, { className: "h-3.5 w-3.5", "aria-hidden": "true" }), children: t('analyticsTranscriptTurnDetails.metadata') }), hasModel && (jsx("div", { "data-testid": "transcript-turn-model", className: "mb-2", children: (renderModel !== null && renderModel !== void 0 ? renderModel : defaultRenderModel)({ model, provider }) })), jsx(DetailRows, { entries: rest, labelFor: labelFor })] }));
260096
+ }
260097
+ function RequestContextSection({ requestContext }) {
260098
+ const t = useT();
260099
+ const labelFor = useFieldLabels();
260100
+ return (jsxs("section", { "data-testid": "transcript-turn-request-context", children: [jsx(SectionHeading, { icon: jsx(Info$3, { className: "h-3.5 w-3.5", "aria-hidden": "true" }), children: t('analyticsTranscriptTurnDetails.requestContext') }), jsx(DetailRows, { entries: requestContext, labelFor: labelFor })] }));
260101
+ }
260102
+ /**
260103
+ * The panel's defaults are the chat's own components, so admins see exactly
260104
+ * what the conversation shows: the "Retrieved Documents" dialog button, the
260105
+ * "Used N tools" record (with results), the nav-bar model badge and the
260106
+ * attachment file cards.
260107
+ */
260108
+ const defaultRenderDocuments = (documents) => (jsx(RetrievedDocumentsButton, { documents: toRetrievedDocuments(documents) }));
260109
+ const defaultRenderToolCalls = (toolCalls) => (jsx(ToolCallIndicator, { toolCalls: toToolCallInfos(toolCalls), showResults: true }));
260110
+ const defaultRenderModel = ({ model, provider }) => (jsx(LlmModelBadge, { model: model, provider: provider }));
260111
+ const defaultRenderFiles = (files) => (jsx(TranscriptFileCards, { files: files }));
260112
+ /**
260113
+ * Collapsible "Sources / Tools / Metadata" panel for one AI turn of an admin
260114
+ * transcript (Perplexity/Claude-style citations). Driven by the extended
260115
+ * per-turn context the backend attaches on admin surfaces — the analytics
260116
+ * `messages/details/` endpoint and the admin `chat-history/` list — and
260117
+ * renders nothing at all when every field is null or empty, so it is safe to
260118
+ * drop under every AI bubble.
260119
+ */
260120
+ function TranscriptTurnDetails({ turn, className, defaultOpen = false, renderDocuments, renderToolCalls, renderModel, renderFiles, }) {
260121
+ const t = useT();
260122
+ const labelFor = useFieldLabels();
260123
+ const [open, setOpen] = React__default.useState(defaultOpen);
260124
+ const attachments = React__default.useMemo(() => [...asArray(turn === null || turn === void 0 ? void 0 : turn.human_files), ...asArray(turn === null || turn === void 0 ? void 0 : turn.ai_files)], [turn === null || turn === void 0 ? void 0 : turn.human_files, turn === null || turn === void 0 ? void 0 : turn.ai_files]);
260125
+ const detailContext = React__default.useMemo(() => ({ labelFor, attachments, renderFiles }), [labelFor, attachments, renderFiles]);
260126
+ if (!hasTranscriptTurnDetails(turn))
260127
+ return null;
260128
+ const documents = asArray(turn === null || turn === void 0 ? void 0 : turn.documents);
260129
+ const toolCalls = asArray(turn === null || turn === void 0 ? void 0 : turn.tool_calls);
260130
+ const metadata = hasEntries(turn === null || turn === void 0 ? void 0 : turn.metadata) ? turn === null || turn === void 0 ? void 0 : turn.metadata : null;
260131
+ const requestContext = hasEntries(turn === null || turn === void 0 ? void 0 : turn.request_context) ? turn === null || turn === void 0 ? void 0 : turn.request_context : null;
260132
+ return (jsx(DetailCtx.Provider, { value: detailContext, children: jsxs(Collapsible, { open: open, onOpenChange: setOpen, className: cn('mt-2', className), "data-testid": "transcript-turn-details", children: [jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [jsx(CollapsibleTrigger, { asChild: true, children: jsxs(Button$1, { type: "button", variant: "outline", size: "sm", className: "flex items-center gap-2 border-gray-200 bg-white text-gray-700 hover:bg-gray-50", "aria-expanded": open, children: [jsx(ChevronDown, { className: cn('h-4 w-4 transition-transform', open && 'rotate-180'), "aria-hidden": "true" }), open
260133
+ ? t('analyticsTranscriptTurnDetails.hideDetails')
260134
+ : t('analyticsTranscriptTurnDetails.showDetails')] }) }), documents.length > 0 && (jsx("div", { "data-testid": "transcript-turn-sources", className: "contents", children: (renderDocuments !== null && renderDocuments !== void 0 ? renderDocuments : defaultRenderDocuments)(documents) })), jsx(TranscriptRollupBadges, { documentsCount: 0, toolCallsCount: toolCalls.length })] }), jsxs(CollapsibleContent, { className: "mt-2 space-y-5 rounded-lg border border-gray-200 bg-white p-3", children: [toolCalls.length > 0 && (jsx("div", { "data-testid": "transcript-turn-tools", children: (renderToolCalls !== null && renderToolCalls !== void 0 ? renderToolCalls : defaultRenderToolCalls)(toolCalls) })), metadata && jsx(MetadataSection, { metadata: metadata, renderModel: renderModel }), requestContext && jsx(RequestContextSection, { requestContext: requestContext })] })] }) }));
260135
+ }
260136
+ /**
260137
+ * "Documents · N" / "Tools · N" chips for a conversation row, from the
260138
+ * conversation-list rollup fields (or a client-side `summarizeTranscriptTurns`).
260139
+ * A chip is hidden when its count is zero; nothing renders when both are.
260140
+ */
260141
+ function TranscriptRollupBadges({ documentsCount, toolCallsCount, className, }) {
260142
+ const t = useT();
260143
+ const documents = toRollupCount(documentsCount);
260144
+ const tools = toRollupCount(toolCallsCount);
260145
+ if (documents === 0 && tools === 0)
260146
+ return null;
260147
+ return (jsxs("span", { className: cn('inline-flex flex-wrap items-center gap-1.5', className), "data-testid": "transcript-rollup-badges", children: [documents > 0 && (jsxs(Badge, { variant: "outline", className: "gap-1 border-transparent bg-blue-50 font-normal text-blue-700", "data-testid": "transcript-sources-badge", children: [jsx(FileText, { className: "h-3 w-3", "aria-hidden": "true" }), t('analyticsTranscriptTurnDetails.sourcesBadge', { count: documents })] })), tools > 0 && (jsxs(Badge, { variant: "outline", className: "gap-1 border-transparent bg-amber-50 font-normal text-amber-800", "data-testid": "transcript-tools-badge", children: [jsx(Wrench, { className: "h-3 w-3", "aria-hidden": "true" }), t('analyticsTranscriptTurnDetails.toolsBadge', { count: tools })] }))] }));
260148
+ }
260149
+
260150
+ /**
260151
+ * One place that decides how a user is shown.
260152
+ *
260153
+ * Backend payloads name their user in a dozen ways — `email` / `lti_email` /
260154
+ * `user_email`, `username` / `student` / `lti_username`, `name` /
260155
+ * `full_name` / `user_full_name`, or a nested `user` object — and most of
260156
+ * them can be empty, `null`, or a placeholder like "None". Every surface used
260157
+ * to pick through those on its own. `resolveUserIdentity()` takes whatever
260158
+ * the payload is and returns what to show, what to open a profile on, and
260159
+ * the avatar initial, so the rule lives here and can be tweaked once.
260160
+ *
260161
+ * Default rule: email → full name → username → the anonymous label.
260162
+ */
260163
+ const DEFAULT_USER_IDENTITY_ORDER = ['email', 'name', 'username'];
260164
+ const DEFAULT_ANONYMOUS_LABEL = 'Anonymous';
260165
+ /** Values backends emit instead of "nothing". Compared case-insensitively. */
260166
+ const PLACEHOLDERS = new Set([
260167
+ '',
260168
+ 'none',
260169
+ 'null',
260170
+ 'undefined',
260171
+ 'n/a',
260172
+ 'na',
260173
+ '-',
260174
+ 'anonymous',
260175
+ 'anonymoususer',
260176
+ ]);
260177
+ /**
260178
+ * `local@domain.tld` with no whitespace, checked with a single pass rather than
260179
+ * a backtracking pattern (CodeQL flags `[^@]+@[^@]+\.[^@]+` as polynomial on
260180
+ * adversarial input).
260181
+ */
260182
+ const isEmailLike = (value) => {
260183
+ const text = value.trim();
260184
+ if (!text || /\s/.test(text))
260185
+ return false;
260186
+ const at = text.indexOf('@');
260187
+ if (at <= 0 || at !== text.lastIndexOf('@') || at === text.length - 1)
260188
+ return false;
260189
+ const domain = text.slice(at + 1);
260190
+ const dot = domain.indexOf('.');
260191
+ // A dot somewhere inside the domain, with something after the last one.
260192
+ return dot > 0 && !domain.endsWith('.');
260193
+ };
260194
+ /** Trimmed string, or undefined for blanks, non-strings and placeholders. */
260195
+ const clean = (value) => {
260196
+ if (typeof value !== 'string')
260197
+ return undefined;
260198
+ const trimmed = value.trim();
260199
+ return PLACEHOLDERS.has(trimmed.toLowerCase()) ? undefined : trimmed;
260200
+ };
260201
+ const firstOf = (...candidates) => {
260202
+ for (const candidate of candidates) {
260203
+ const value = clean(candidate);
260204
+ if (value)
260205
+ return value;
260206
+ }
260207
+ return undefined;
260208
+ };
260209
+ const nested = (source) => source.user && typeof source.user === 'object' ? source.user : undefined;
260210
+ /**
260211
+ * Resolve how a user should be displayed from any payload that names one.
260212
+ *
260213
+ * - `email`: `lti_email`, `email`, `user_email`, the nested `user`'s email —
260214
+ * or a username field that turns out to hold an email address.
260215
+ * - `username`: `lti_username`, `username`, `student`, `user_username`, the
260216
+ * nested `user`'s username, or a bare string/number `user` value.
260217
+ * - `name`: `user_full_name`, `full_name`, `name`, `first_name last_name`, or
260218
+ * the nested `user`'s name.
260219
+ * - `profileUsername`: the first *platform* username (never an LTI claim).
260220
+ *
260221
+ * A string source is treated as an email when it looks like one, else as a
260222
+ * username. Placeholder strings ("None", "-", "anonymous"…) count as absent.
260223
+ */
260224
+ function resolveUserIdentity(source, options = {}) {
260225
+ var _a;
260226
+ const anonymousLabel = options.anonymousLabel || DEFAULT_ANONYMOUS_LABEL;
260227
+ const order = ((_a = options.order) === null || _a === void 0 ? void 0 : _a.length) ? options.order : DEFAULT_USER_IDENTITY_ORDER;
260228
+ const record = source && typeof source === 'object' ? source : { user: source !== null && source !== void 0 ? source : null };
260229
+ const inner = nested(record);
260230
+ // A bare `user` value is an id or username; numeric ids are still shown.
260231
+ const bareUser = typeof record.user === 'number' && Number.isFinite(record.user)
260232
+ ? String(record.user)
260233
+ : typeof record.user === 'string'
260234
+ ? clean(record.user)
260235
+ : undefined;
260236
+ let email = firstOf(record.lti_email, record.email, record.user_email, inner === null || inner === void 0 ? void 0 : inner.lti_email, inner === null || inner === void 0 ? void 0 : inner.email, inner === null || inner === void 0 ? void 0 : inner.user_email);
260237
+ const platformUsername = firstOf(record.username, record.student, record.user_username, inner === null || inner === void 0 ? void 0 : inner.username, inner === null || inner === void 0 ? void 0 : inner.student, inner === null || inner === void 0 ? void 0 : inner.user_username, bareUser && !isEmailLike(bareUser) ? bareUser : undefined);
260238
+ let username = firstOf(record.lti_username, inner === null || inner === void 0 ? void 0 : inner.lti_username, platformUsername);
260239
+ const composed = [clean(record.first_name), clean(record.last_name)].filter(Boolean).join(' ');
260240
+ const name = firstOf(record.user_full_name, record.full_name, record.name, composed, inner === null || inner === void 0 ? void 0 : inner.user_full_name, inner === null || inner === void 0 ? void 0 : inner.full_name, inner === null || inner === void 0 ? void 0 : inner.name);
260241
+ // A bare string that looks like an email is an email, and so is a
260242
+ // "username" field that actually carries one.
260243
+ if (!email && bareUser && isEmailLike(bareUser))
260244
+ email = bareUser;
260245
+ if (!email && username && isEmailLike(username)) {
260246
+ email = username;
260247
+ username = undefined;
260248
+ }
260249
+ const facts = { email, username, name };
260250
+ const label = order.map((field) => facts[field]).find(Boolean);
260251
+ return {
260252
+ label: label !== null && label !== void 0 ? label : anonymousLabel,
260253
+ email,
260254
+ username,
260255
+ name,
260256
+ profileUsername: platformUsername,
260257
+ initial: (label !== null && label !== void 0 ? label : anonymousLabel).charAt(0).toUpperCase() || 'A',
260258
+ isAnonymous: !label,
260259
+ };
260260
+ }
260261
+
258128
260262
  const AnalyticsSettingsContext = createContext({});
258129
260263
  AnalyticsSettingsContext.Provider;
258130
260264
 
@@ -260865,64 +262999,6 @@ const AutoResizeTextarea = ({ allowEmptySubmit = false, value, onChange, onSubmi
260865
262999
  disabled, placeholder: placeholder, style: combinedStyle, className: `flex max-h-32 min-h-[36px] w-full resize-none rounded-2xl border-0 border-gray-200 bg-transparent px-4 py-2 pt-2 pb-1 text-base ring-offset-white placeholder:text-gray-400 focus:outline-none focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-800 dark:bg-gray-950 dark:ring-offset-gray-950 dark:placeholder:text-gray-400 ${className}`, ...props }));
260866
263000
  };
260867
263001
 
260868
- function DocumentSidebar({ isModal = false, sessionId }) {
260869
- const t = useT();
260870
- const [isCollapsed, setIsCollapsed] = React.useState(false);
260871
- const params = useParams();
260872
- const username = useUsername();
260873
- const isStreaming = useSelector(selectStreaming);
260874
- const { data: vectorDocuments, refetch, status, } = useGetVectorDocumentsQuery({
260875
- org: params.tenantKey,
260876
- sessionId: sessionId,
260877
- // @ts-ignore
260878
- userId: username,
260879
- }, {
260880
- skip: !params.tenantKey || !sessionId || !username || !isLoggedIn(),
260881
- });
260882
- React.useEffect(() => {
260883
- if (!isStreaming && isLoggedIn() && status !== 'uninitialized') {
260884
- refetch();
260885
- }
260886
- }, [isStreaming, refetch]);
260887
- const toggleCollapse = () => {
260888
- setIsCollapsed(!isCollapsed);
260889
- };
260890
- return (jsx(Fragment$1, { children: (vectorDocuments === null || vectorDocuments === void 0 ? void 0 : vectorDocuments.length) && vectorDocuments.length > 0 ? (jsx("aside", { className: cn('transition-all duration-300 ease-in-out', isModal
260891
- ? 'max-h-full w-full overflow-auto'
260892
- : 'hidden rounded-tl-lg rounded-bl-lg md:hidden lg:block', !isModal && (isCollapsed ? 'w-[40px]' : 'w-[380px]')), style: {
260893
- backgroundColor: isCollapsed && !isModal ? 'white' : '#F3F6FB',
260894
- maxHeight: 'calc(100vh - 80px)',
260895
- }, children: jsxs("div", { className: "flex h-full flex-col", children: [!isModal && (jsxs("div", { className: cn('flex cursor-pointer items-center gap-2 border-b border-gray-200 p-4 transition-colors duration-200', isCollapsed && 'h-[80px] justify-center rounded-tl-[5px] rounded-bl-[5px] p-2', 'hover:bg-blue-100'), onClick: toggleCollapse, style: { backgroundColor: '#F3F6FB' }, children: [jsx(FileText, { className: "h-5 w-5 shrink-0 text-blue-600" }), !isCollapsed && (jsxs(Fragment$1, { children: [jsx("h2", { className: "text-base font-medium text-[#646464]", children: t('documentSidebar.retrievedDocuments') }), jsx(ChevronRight, { className: cn('ml-auto h-4 w-4 text-gray-400 transition-transform', isCollapsed ? 'rotate-180' : '') })] }))] })), (!isCollapsed || isModal) && (jsx(Fragment$1, { children: jsx("div", { className: "flex-1 overflow-auto", style: { backgroundColor: '#F3F6FB' }, children: jsx("div", { className: "mt-0 space-y-1 p-2", children: vectorDocuments.map((doc, index) => {
260896
- var _a, _b;
260897
- return (jsxs(React.Fragment, { children: [jsx("div", { className: "rounded-lg p-2 hover:bg-gray-50", children: jsxs("div", { className: "flex items-start gap-2", children: [jsx(Radio$1, { className: "mt-1 h-4 w-4 text-gray-400" }), jsxs("div", { className: "min-w-0 flex-1 space-y-1", children: [jsxs("div", { className: "flex items-center justify-between", children: [jsx(Link$2, { href: (_a = doc.source) !== null && _a !== void 0 ? _a : '', target: "_blank", className: "min-w-0", children: jsx("span", { className: "text-sm font-semibold break-words text-[#646464] hover:text-blue-600", children: doc.title }) }), jsx("div", { className: "flex items-center gap-2 self-center", children: jsx("span", { className: "text-xs text-gray-500", children: (_b = doc === null || doc === void 0 ? void 0 : doc.score) === null || _b === void 0 ? void 0 : _b.toFixed(2) }) })] }), jsx("p", { className: "line-clamp-6 pt-[10px] text-[14px] text-gray-600", children: doc.snippet })] })] }) }), index < vectorDocuments.length - 1 && (jsx("div", { className: "my-2 border-t border-gray-200" }))] }, index));
260898
- }) }) }) }))] }) })) : null }));
260899
- }
260900
-
260901
- function RetrievedDocumentsButton({ sessionId }) {
260902
- const t = useT();
260903
- const [isOpen, setIsOpen] = useState(false);
260904
- const params = useParams();
260905
- const username = useUsername();
260906
- const isStreaming = useSelector(selectStreaming);
260907
- const { data: vectorDocuments, refetch, status, } = useGetVectorDocumentsQuery({
260908
- org: params.tenantKey,
260909
- sessionId: sessionId,
260910
- // @ts-ignore
260911
- userId: username,
260912
- }, {
260913
- skip: !params.tenantKey || !sessionId || !username || !isLoggedIn(),
260914
- });
260915
- useEffect(() => {
260916
- if (!isStreaming && isLoggedIn() && status !== 'uninitialized') {
260917
- refetch();
260918
- }
260919
- }, [isStreaming, refetch]);
260920
- if (!(vectorDocuments === null || vectorDocuments === void 0 ? void 0 : vectorDocuments.length)) {
260921
- return null;
260922
- }
260923
- return (jsxs(Fragment$1, { children: [jsxs(Button$1, { variant: "outline", size: "sm", className: "flex items-center gap-2 border-gray-200 bg-white text-gray-700 hover:bg-gray-50", onClick: () => setIsOpen(true), children: [jsx(FileText, { className: "h-4 w-4 text-blue-600" }), jsx("span", { children: t('retrievedDocumentsButton.title') })] }), jsx(Dialog, { open: isOpen, onOpenChange: setIsOpen, children: jsxs(DialogContent, { className: "flex max-h-[80vh] flex-col gap-0 p-0 sm:max-w-[425px] md:max-w-[600px]", children: [jsx(DialogHeader, { className: "border-b px-4 py-2", children: jsxs(DialogTitle, { className: "flex items-center gap-2 text-base font-medium text-[#646464]", children: [jsx(FileText, { className: "h-5 w-5 text-blue-600" }), t('retrievedDocumentsButton.title')] }) }), jsx("div", { className: "flex-1 overflow-hidden", children: jsx(DocumentSidebar, { isModal: true, sessionId: sessionId }) })] }) })] }));
260924
- }
260925
-
260926
263002
  const PAGE_SIZE$5 = 10;
260927
263003
  const MemoryMenu = ({ onClose, mentorId, tenantKey, username }) => {
260928
263004
  const t = useT();
@@ -263583,7 +265659,7 @@ function useWebsiteCrawlerResource(resource) {
263583
265659
  function WebsiteCrawlModal({ resource }) {
263584
265660
  const t = useT();
263585
265661
  const { form, handleCheckUrlIsValid, crawlerMatchPatterns, setCrawlerMatchPatterns } = useWebsiteCrawlerResource(resource);
263586
- return (jsxs(Card, { className: "mx-auto w-full max-w-2xl", role: "dialog", "aria-labelledby": "crawler-title", "aria-describedby": "crawler-description", children: [jsxs(CardHeader, { children: [jsx(CardTitle, { id: "crawler-title", className: "text-md font-semibold text-gray-600", children: t('resourceModalWebsiteCrawlModal.title') }), jsx("p", { id: "crawler-description", className: "text-muted-foreground text-sm", children: t('resourceModalWebsiteCrawlModal.description') })] }), jsx(CardContent, { children: jsx("form", { className: "space-y-4", onSubmit: (e) => {
265662
+ return (jsxs(Card$1, { className: "mx-auto w-full max-w-2xl", role: "dialog", "aria-labelledby": "crawler-title", "aria-describedby": "crawler-description", children: [jsxs(CardHeader, { children: [jsx(CardTitle, { id: "crawler-title", className: "text-md font-semibold text-gray-600", children: t('resourceModalWebsiteCrawlModal.title') }), jsx("p", { id: "crawler-description", className: "text-muted-foreground text-sm", children: t('resourceModalWebsiteCrawlModal.description') })] }), jsx(CardContent, { children: jsx("form", { className: "space-y-4", onSubmit: (e) => {
263587
265663
  e.preventDefault();
263588
265664
  e.stopPropagation();
263589
265665
  form.handleSubmit();
@@ -264943,8 +267019,21 @@ function BenchmarksTab({ tenantKey, username, }) {
264943
267019
  }), onClick: () => setViewBenchmark(benchmark.name), children: jsx(Eye, { className: "h-4 w-4" }) }) }) })] }, benchmark.name))), filtered.length === 0 && (jsx("tr", { children: jsx("td", { colSpan: 4, className: "px-6 py-8 text-center text-gray-500 dark:text-gray-400", children: isFetching ? t('benchmarksIndex.loading') : t('benchmarksIndex.empty') }) }))] })] }) }), totalPages > 1 && (jsx("div", { className: "mt-6", children: jsx(AdvancedPagination, { totalPages: totalPages, currentPage: page, onPageChange: setPage }) }))] })), jsx(CreateBenchmarkModal, { isOpen: showCreate, onClose: () => setShowCreate(false), tenantKey: tenantKey, username: username }), viewBenchmark && (jsx(BenchmarkItemsModal, { isOpen: !!viewBenchmark, onClose: () => setViewBenchmark(null), tenantKey: tenantKey, username: username, datasetName: viewBenchmark }))] }));
264944
267020
  }
264945
267021
 
267022
+ const TAB_DESC_KEYS = {
267023
+ management: 'descManagement',
267024
+ organization: 'descOrganization',
267025
+ integrations: 'descIntegrations',
267026
+ billing: 'descBilling',
267027
+ monetization: 'descMonetization',
267028
+ datasets: 'descBenchmarks',
267029
+ memory: 'descMemory',
267030
+ advanced: 'descAdvanced',
267031
+ basic: 'descBasic',
267032
+ social: 'descSocial',
267033
+ security: 'descSecurity',
267034
+ };
264946
267035
  function Account({ tenant, tenants = [], username, onInviteClick, email, mainPlatformKey, userActiveApp = null, showPlatformName = false, isAdmin = false, targetTab = 'basic', currentPlan: _currentPlan = '', currentSPA = '', authURL, onTenantUpdate, currentPlatformBaseDomain = '', enableRbac = false, showBenchmarks = false, rbacPermissions = {}, onLoadGroupPermissions, onTabChange, defaultSupportPhone = '', enableSupportPhone, showGradebookTab = false, onboardingBasePath = '', }) {
264947
- var _a, _b, _c, _d, _e;
267036
+ var _a, _b, _c;
264948
267037
  const t = useT();
264949
267038
  const currentTenant = tenants.find((ten) => ten.key === tenant);
264950
267039
  const hasMonetizationPermission = checkRbacPermission(rbacPermissions, `/platforms/${tenant}/#can_sell_items`, enableRbac);
@@ -264957,17 +267046,46 @@ function Account({ tenant, tenants = [], username, onInviteClick, email, mainPla
264957
267046
  */
264958
267047
  const canSeeBilling = !!(currentTenant === null || currentTenant === void 0 ? void 0 : currentTenant.show_paywall) && (isAdmin || hasMonetizationPermission);
264959
267048
  const TABS = [
264960
- { id: 'organization', label: t('profileAccount.tabOrganization'), icon: Briefcase },
264961
- { id: 'management', label: t('profileAccount.tabManagement'), icon: Users },
264962
- { id: 'integrations', label: t('profileAccount.tabIntegrations'), icon: KeyRound },
267049
+ {
267050
+ id: 'organization',
267051
+ label: t('profileAccount.tabOrganization'),
267052
+ icon: Briefcase,
267053
+ group: 'workspace',
267054
+ },
267055
+ {
267056
+ id: 'management',
267057
+ label: t('profileAccount.tabManagement'),
267058
+ icon: Users,
267059
+ group: 'workspace',
267060
+ },
267061
+ {
267062
+ id: 'integrations',
267063
+ label: t('profileAccount.tabIntegrations'),
267064
+ icon: KeyRound,
267065
+ group: 'workspace',
267066
+ },
264963
267067
  ...(canMonetize
264964
- ? [{ id: 'monetization', label: t('profileAccount.tabMonetization'), icon: Coins }]
267068
+ ? [
267069
+ {
267070
+ id: 'monetization',
267071
+ label: t('profileAccount.tabMonetization'),
267072
+ icon: Coins,
267073
+ group: 'finance',
267074
+ },
267075
+ ]
264965
267076
  : []),
264966
267077
  ...(showBenchmarks
264967
- ? [{ id: 'datasets', label: t('profileAccount.tabBenchmarks'), icon: ListChecks }]
267078
+ ? [
267079
+ {
267080
+ id: 'datasets',
267081
+ label: t('profileAccount.tabBenchmarks'),
267082
+ icon: ListChecks,
267083
+ group: 'aiData',
267084
+ },
267085
+ ]
264968
267086
  : []),
264969
- { id: 'memory', label: t('profileAccount.tabMemory'), icon: Brain },
264970
- { id: 'advanced', label: t('profileAccount.tabAdvanced'), icon: Settings$1 },
267087
+ { id: 'memory', label: t('profileAccount.tabMemory'), icon: Brain, group: 'aiData' },
267088
+ { id: 'advanced', label: t('profileAccount.tabAdvanced'), icon: Settings$1, group: 'system' },
264971
267089
  ];
264972
267090
  const [organizationLogo, setOrganizationLogo] = useState(`${LOGO_ENDPOINTS.light_logo(tenant)}?v=${Date.now()}`);
264973
267091
  const hasUserTabPermission = checkRbacPermission(rbacPermissions, `/platforms/${tenant}/#can_manage_users`, enableRbac);
@@ -265012,56 +267130,61 @@ function Account({ tenant, tenants = [], username, onInviteClick, email, mainPla
265012
267130
  firstInput.focus();
265013
267131
  }
265014
267132
  }, []);
265015
- return (jsxs("div", { className: "flex flex-col lg:flex-row h-full", children: [jsx("div", { className: "lg:hidden", children: jsx("div", { className: "px-6 py-4 border-b border-gray-200", children: jsx("h2", { className: "text-lg font-semibold text-gray-900", children: t('profileAccount.profileTitle') }) }) }), jsxs("div", { className: "hidden lg:flex bg-white border-r border-gray-200 dark:border-gray-700 flex-col overflow-hidden", style: {
265016
- width: '320px',
265017
- height: '100%',
265018
- }, children: [jsxs("div", { className: "flex flex-col items-center justify-center py-6 px-6 border-b border-gray-200 dark:border-gray-700 flex-shrink-0", children: [organizationLogo ? (jsx("div", { className: "w-auto h-auto max-w-[140px] max-h-[140px] overflow-hidden", children: jsx(Image$4, { src: organizationLogo, alt: t('profileAccount.organizationLogoAlt'), width: 140, height: 140, className: "object-cover w-full h-full group-hover:opacity-80 transition-opacity", onError: () => {
265019
- setOrganizationLogo('');
265020
- } }) })) : (jsx(BriefcaseBusiness, { className: "h-20 w-20 mb-4 text-blue-500" })), jsxs("div", { className: "flex justify-center space-x-3 mt-3", children: [jsxs("div", { className: "bg-blue-100 text-blue-700 px-2 py-1 rounded font-medium flex items-center text-sm", children: [jsx(User, { className: "h-4 w-4 mr-2" }), isAdmin ? t('profileAccount.roleAdmin') : t('profileAccount.roleUser')] }), showPlatformName && (jsx("div", { className: "bg-gray-100 text-gray-600 px-4 py-2 rounded text-xs font-medium", children: tenant.toUpperCase() }))] })] }), jsx("div", { className: "flex-1 overflow-y-auto", style: { minHeight: 0 }, children: jsx("div", { className: "p-4", children: jsxs("div", { className: "flex flex-col space-y-1.5", children: [TABS.filter((tab) => visibleTabIds.includes(tab.id)).map((tab) => (jsxs("button", { onClick: () => {
265021
- setActiveTab(tab.id);
265022
- // Clear URL param when switching away from billing/monetization
265023
- if ((activeTab === 'billing' || activeTab === 'monetization') &&
265024
- tab.id !== 'billing' &&
265025
- tab.id !== 'monetization') {
265026
- onTabChange === null || onTabChange === void 0 ? void 0 : onTabChange('');
265027
- }
265028
- if (tab.id === 'monetization') {
265029
- onTabChange === null || onTabChange === void 0 ? void 0 : onTabChange('monetization');
265030
- }
265031
- }, className: `w-full justify-start px-4 py-2.5 text-left rounded-lg transition-all flex items-center text-base ${activeTab === tab.id
265032
- ? 'bg-blue-50 text-blue-700 font-medium'
265033
- : 'hover:bg-gray-50 text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100'}`, children: [jsx(tab.icon, { className: "h-5 w-5 mr-3 flex-shrink-0" }), jsx("span", { className: "truncate", children: tab.label })] }, tab.id))), canSeeBilling && (jsxs("button", { onClick: () => {
265034
- setActiveTab('billing');
265035
- // Set URL param for billing tab
265036
- onTabChange === null || onTabChange === void 0 ? void 0 : onTabChange('billing');
265037
- }, className: `w-full justify-start px-4 py-2.5 text-left rounded-lg transition-all flex items-center text-base ${activeTab === 'billing'
265038
- ? 'bg-blue-50 text-blue-700 font-medium'
265039
- : 'hover:bg-gray-50 text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100'}`, children: [jsx(CreditCard, { className: "h-5 w-5 mr-3 flex-shrink-0" }), jsx("span", { className: "truncate", children: t('profileAccount.tabBilling') })] }, 'billing'))] }) }) })] }), jsx("div", { className: "lg:hidden", children: jsx("div", { className: "w-full justify-start px-6 py-2 bg-white border-b border-gray-200 rounded-none h-auto overflow-x-auto", children: jsxs("div", { className: "flex space-x-2", children: [TABS.filter((tab) => visibleTabIds.includes(tab.id)).map((tab) => (jsxs("button", { onClick: () => {
265040
- setActiveTab(tab.id);
265041
- // Clear URL param when switching away from billing/monetization
265042
- if ((activeTab === 'billing' || activeTab === 'monetization') &&
265043
- tab.id !== 'billing' &&
265044
- tab.id !== 'monetization') {
265045
- onTabChange === null || onTabChange === void 0 ? void 0 : onTabChange('');
265046
- }
265047
- if (tab.id === 'monetization') {
265048
- onTabChange === null || onTabChange === void 0 ? void 0 : onTabChange('monetization');
265049
- }
265050
- }, className: `flex items-center gap-2 px-3 py-2 rounded-lg whitespace-nowrap text-sm transition-all ${activeTab === tab.id
265051
- ? 'bg-blue-50 text-blue-600 font-medium'
265052
- : 'text-gray-600 hover:bg-gray-50'}`, children: [jsx(tab.icon, { className: "h-4 w-4" }), jsx("span", { className: "hidden sm:inline", children: tab.label }), jsx("span", { className: "sm:hidden", children: tab.label })] }, tab.id))), canSeeBilling && (jsxs("button", { onClick: () => {
265053
- setActiveTab('billing');
265054
- // Set URL param for billing tab
265055
- onTabChange === null || onTabChange === void 0 ? void 0 : onTabChange('billing');
265056
- }, className: `flex items-center gap-2 px-3 py-2 rounded-lg whitespace-nowrap text-sm transition-all ${activeTab === 'billing'
265057
- ? 'bg-blue-50 text-blue-600 font-medium'
265058
- : 'text-gray-600 hover:bg-gray-50'}`, children: [jsx(CreditCard, { className: "h-4 w-4" }), jsx("span", { className: "hidden sm:inline", children: t('profileAccount.tabBilling') }), jsx("span", { className: "sm:hidden", children: t('profileAccount.tabBilling') })] }, 'billing'))] }) }) }), jsxs("div", { className: "flex-1 flex flex-col overflow-hidden", style: { height: '100%' }, children: [jsxs("div", { className: "hidden lg:block flex-shrink-0 p-6 border-b border-gray-200 bg-white dark:bg-gray-900", children: [jsx("h3", { className: "text-lg font-medium text-gray-900 dark:text-gray-100 mb-2", children: (_c = (_b = TABS.find((t) => t.id === activeTab)) === null || _b === void 0 ? void 0 : _b.label) !== null && _c !== void 0 ? _c : (activeTab === 'billing' ? t('profileAccount.tabBilling') : activeTab) }), jsxs("p", { className: "text-gray-600 dark:text-gray-400 text-sm", children: [activeTab === 'management' && t('profileAccount.descManagement'), activeTab === 'organization' && t('profileAccount.descOrganization'), activeTab === 'integrations' && t('profileAccount.descIntegrations'), activeTab === 'billing' && t('profileAccount.descBilling'), activeTab === 'monetization' && t('profileAccount.descMonetization'), activeTab === 'datasets' && t('profileAccount.descBenchmarks'), activeTab === 'memory' && t('profileAccount.descMemory'), activeTab === 'advanced' && t('profileAccount.descAdvanced')] })] }), jsxs("div", { className: "flex-1 p-6 space-y-6", style: {
265059
- overflowY: 'auto',
265060
- overflowX: 'hidden',
265061
- }, children: [jsxs("div", { className: "lg:hidden mb-6", children: [jsx("h3", { className: "text-lg font-medium text-gray-900 dark:text-gray-100 mb-2", children: (_e = (_d = TABS.find((t) => t.id === activeTab)) === null || _d === void 0 ? void 0 : _d.label) !== null && _e !== void 0 ? _e : (activeTab === 'billing' ? t('profileAccount.tabBilling') : activeTab) }), jsxs("p", { className: "text-gray-600 dark:text-gray-400 text-sm", children: [activeTab === 'billing' && t('profileAccount.descBilling'), activeTab === 'basic' && t('profileAccount.descBasic'), activeTab === 'social' && t('profileAccount.descSocial'), activeTab === 'security' && t('profileAccount.descSecurity'), activeTab === 'management' && t('profileAccount.descManagement'), activeTab === 'organization' && t('profileAccount.descOrganization'), activeTab === 'monetization' && t('profileAccount.descMonetization'), activeTab === 'datasets' && t('profileAccount.descBenchmarks'), activeTab === 'memory' && t('profileAccount.descMemory'), activeTab === 'advanced' && t('profileAccount.descAdvanced')] })] }), activeTab === 'management' && hasManagementPermissions && (jsx(Admin, { onboardingBasePath: onboardingBasePath, authSpaUrl: authURL, onInviteClick: onInviteClick, tenant: tenant, currentSpa: currentSPA, hasUserTabPermission: hasUserTabPermission, hasGroupsTabPermission: hasGroupsTabPermission, hasPoliciesTabPermission: hasPoliciesTabPermission, hasRolesTabPermission: hasRolesTabPermission, hasTeamsTabPermission: hasTeamsTabPermission, hasInviteUserPermission: hasInviteUserPermission, hasCreateTeamPermission: hasCreateTeamPermission, onLoadGroupPermissions: onLoadGroupPermissions, hasAlertsTabPermission: hasAlertsTabPermission, rbacPermissions: rbacPermissions, enableRbac: enableRbac, showGradebookTab: showGradebookTab })), activeTab === 'organization' && (jsx(OrganizationTab, { platformKey: tenant, tenant: tenants.find((ten) => ten.key === tenant), onTenantUpdate: onTenantUpdate, setOrganizationLogoFromOutside: setOrganizationLogo, defaultSupportPhone: defaultSupportPhone, enableSupportPhone: enableSupportPhone })), activeTab === 'integrations' && (jsx(IntegrationsTab, { tenantKey: tenant, username: username })), activeTab === 'billing' && canSeeBilling && (jsx(BillingTab, { tenant: tenant, userActiveApp: userActiveApp, username: username, currentUserEmail: email, mainPlatformKey: mainPlatformKey })), activeTab === 'monetization' && (jsx(MonetizationTab, { platformKey: tenant, authURL: authURL })), activeTab === 'datasets' && showBenchmarks && (jsx(BenchmarksTab, { tenantKey: tenant, username: username })), activeTab === 'memory' && jsx(MemoryAdminTab, { tenantKey: tenant, username: username }), activeTab === 'advanced' && (jsx(AdvancedTab, { platformKey: tenant, currentSPA: currentSPA, username: username, authURL: authURL, currentPlatformBaseDomain: currentPlatformBaseDomain }))] })] }), jsx(ToastProvider, {})] }));
267133
+ /**
267134
+ * Billing and Monetization are mirrored into the host's URL through
267135
+ * `onTabChange`; leaving them for any other tab clears the param.
267136
+ */
267137
+ const selectTab = (id) => {
267138
+ setActiveTab(id);
267139
+ if (id === 'billing') {
267140
+ onTabChange === null || onTabChange === void 0 ? void 0 : onTabChange('billing');
267141
+ return;
267142
+ }
267143
+ if ((activeTab === 'billing' || activeTab === 'monetization') &&
267144
+ id !== 'billing' &&
267145
+ id !== 'monetization') {
267146
+ onTabChange === null || onTabChange === void 0 ? void 0 : onTabChange('');
267147
+ }
267148
+ if (id === 'monetization') {
267149
+ onTabChange === null || onTabChange === void 0 ? void 0 : onTabChange('monetization');
267150
+ }
267151
+ };
267152
+ const billingTab = {
267153
+ id: 'billing',
267154
+ label: t('profileAccount.tabBilling'),
267155
+ icon: CreditCard,
267156
+ group: 'finance',
267157
+ };
267158
+ const navTabs = [...TABS, billingTab].filter((tab) => visibleTabIds.includes(tab.id));
267159
+ const GROUP_LABELS = {
267160
+ workspace: t('profileAccount.groupWorkspace'),
267161
+ finance: t('profileAccount.groupFinance'),
267162
+ aiData: t('profileAccount.groupAiData'),
267163
+ system: t('profileAccount.groupSystem'),
267164
+ };
267165
+ const navGroups = Object.keys(GROUP_LABELS).map((group) => ({
267166
+ id: group,
267167
+ label: GROUP_LABELS[group],
267168
+ items: navTabs.filter((tab) => tab.group === group),
267169
+ }));
267170
+ // Render the tab's `label` rather than the id so renames like
267171
+ // "datasets" → "Benchmarks" land in the header too. Billing lives outside
267172
+ // TABS (paywall-gated), so it needs its own translated label — the raw id
267173
+ // would render lowercase.
267174
+ const title = (_c = (_b = TABS.find((tab) => tab.id === activeTab)) === null || _b === void 0 ? void 0 : _b.label) !== null && _c !== void 0 ? _c : (activeTab === 'billing' ? t('profileAccount.tabBilling') : activeTab);
267175
+ const description = TAB_DESC_KEYS[activeTab]
267176
+ ? t(`profileAccount.${TAB_DESC_KEYS[activeTab]}`)
267177
+ : undefined;
267178
+ const platformName = (currentTenant === null || currentTenant === void 0 ? void 0 : currentTenant.platform_name) || (currentTenant === null || currentTenant === void 0 ? void 0 : currentTenant.name) || tenant;
267179
+ const roleChip = (jsx(SettingsChip, { tone: "accent", icon: User, children: isAdmin ? t('profileAccount.roleAdmin') : t('profileAccount.roleUser') }));
267180
+ const sidebarHeader = (jsxs("div", { className: "flex flex-col gap-3.5", children: [jsxs("div", { className: "flex items-center gap-3", children: [organizationLogo ? (jsx("div", { className: "flex h-14 w-14 flex-shrink-0 items-center justify-center overflow-hidden rounded-xl border border-gray-200 bg-white p-1 dark:border-gray-700", children: jsx(Image$4, { src: organizationLogo, alt: t('profileAccount.organizationLogoAlt'), width: 56, height: 56, className: "h-full w-full object-contain", onError: () => {
267181
+ setOrganizationLogo('');
267182
+ } }) })) : (jsx("div", { className: "flex h-14 w-14 flex-shrink-0 items-center justify-center rounded-xl border border-blue-100 bg-blue-50 text-blue-600 dark:border-blue-900 dark:bg-blue-950 dark:text-blue-300", "aria-hidden": "true", children: jsx(BriefcaseBusiness, { className: "h-6 w-6" }) })), jsxs("div", { className: "min-w-0", children: [jsx("h2", { className: "truncate text-[15px] font-semibold text-gray-900 dark:text-gray-100", title: platformName, children: platformName }), platformName !== tenant && (jsx("p", { className: "truncate font-mono text-xs text-gray-500 dark:text-gray-400", children: tenant }))] })] }), jsxs("div", { className: "flex flex-wrap gap-1.5", children: [roleChip, showPlatformName && jsx(SettingsChip, { children: tenant.toUpperCase() })] })] }));
267183
+ const mobileHeader = (jsx("div", { className: "flex min-w-0 items-center justify-between gap-3", children: jsx("p", { className: "truncate text-[15px] font-semibold text-gray-900 dark:text-gray-100", children: platformName || t('profileAccount.profileTitle') }) }));
267184
+ return (jsx(SettingsShell, { sidebarHeader: sidebarHeader, sidebarNav: jsx(SettingsSidebarNav, { groups: navGroups, activeId: activeTab, onSelect: selectTab, variant: "buttons", ariaLabel: t('profileAccount.ariaSettingsNav') }), mobileHeader: mobileHeader, mobileNav: jsx(SettingsMobileNav, { groups: navGroups, activeId: activeTab, onSelect: selectTab, variant: "buttons", ariaLabel: t('profileAccount.ariaSettingsNavMobile') }), title: title, description: description, overlays: jsx(ToastProvider, {}), children: jsxs("div", { className: "space-y-6", children: [activeTab === 'management' && hasManagementPermissions && (jsx(Admin, { onboardingBasePath: onboardingBasePath, authSpaUrl: authURL, onInviteClick: onInviteClick, tenant: tenant, currentSpa: currentSPA, hasUserTabPermission: hasUserTabPermission, hasGroupsTabPermission: hasGroupsTabPermission, hasPoliciesTabPermission: hasPoliciesTabPermission, hasRolesTabPermission: hasRolesTabPermission, hasTeamsTabPermission: hasTeamsTabPermission, hasInviteUserPermission: hasInviteUserPermission, hasCreateTeamPermission: hasCreateTeamPermission, onLoadGroupPermissions: onLoadGroupPermissions, hasAlertsTabPermission: hasAlertsTabPermission, rbacPermissions: rbacPermissions, enableRbac: enableRbac, showGradebookTab: showGradebookTab })), activeTab === 'organization' && (jsx(OrganizationTab, { platformKey: tenant, tenant: tenants.find((ten) => ten.key === tenant), onTenantUpdate: onTenantUpdate, setOrganizationLogoFromOutside: setOrganizationLogo, defaultSupportPhone: defaultSupportPhone, enableSupportPhone: enableSupportPhone })), activeTab === 'integrations' && jsx(IntegrationsTab, { tenantKey: tenant, username: username }), activeTab === 'billing' && canSeeBilling && (jsx(BillingTab, { tenant: tenant, userActiveApp: userActiveApp, username: username, currentUserEmail: email, mainPlatformKey: mainPlatformKey })), activeTab === 'monetization' && jsx(MonetizationTab, { platformKey: tenant, authURL: authURL }), activeTab === 'datasets' && showBenchmarks && (jsx(BenchmarksTab, { tenantKey: tenant, username: username })), activeTab === 'memory' && jsx(MemoryAdminTab, { tenantKey: tenant, username: username }), activeTab === 'advanced' && (jsx(AdvancedTab, { platformKey: tenant, currentSPA: currentSPA, username: username, authURL: authURL, currentPlatformBaseDomain: currentPlatformBaseDomain }))] }) }));
265062
267185
  }
265063
267186
 
265064
- function UserProfileModal({ isOpen, onClose, params, email, mainPlatformKey, showMentorAIDisplayCheckbox = false, showLeaderboardDisplayCheckbox = false, showGradebookTab = false, showUsernameField = false, showPlatformName = false, useGravatarPicFallback = true, enableCatalogInvite = false, targetTab = 'basic', currentPlan = '', currentSPA = '', userActiveApp = null, authURL, tenants = [], onTenantUpdate, currentPlatformBaseDomain = '', rbacPermissions = {}, enableRbac = false, onLoadGroupPermissions, onTabChange, defaultSupportPhone = '', onBillingTabRequest, onAccountDeleted, enableMemoryTab = false, showBenchmarks = false, localLLMProps, enableSupportPhone, onboardingBasePath = '', }) {
267187
+ function UserProfileModal({ isOpen, onClose, params, email, mainPlatformKey, showMentorAIDisplayCheckbox = false, showLeaderboardDisplayCheckbox = false, showGradebookTab = false, showUsernameField = false, showPlatformName = false, useGravatarPicFallback = true, enableCatalogInvite = false, targetTab = 'basic', currentPlan = '', currentSPA = '', userActiveApp = null, authURL, tenants = [], onTenantUpdate, currentPlatformBaseDomain = '', rbacPermissions = {}, enableRbac = false, onLoadGroupPermissions, onTabChange, defaultSupportPhone = '', onBillingTabRequest, onAccountDeleted, enableMemoryTab = false, showLlmUsageTab = true, showBenchmarks = false, localLLMProps, enableSupportPhone, onboardingBasePath = '', }) {
265065
267188
  const t = useT();
265066
267189
  console.log('[UserProfileModal] localLLMProps received:', {
265067
267190
  isAvailable: localLLMProps === null || localLLMProps === void 0 ? void 0 : localLLMProps.isAvailable,
@@ -265111,7 +267234,7 @@ function UserProfileModal({ isOpen, onClose, params, email, mainPlatformKey, sho
265111
267234
  if (!isInviteUserDialogOpen) {
265112
267235
  onClose();
265113
267236
  }
265114
- }, children: jsxs(DialogContent, { autoFocus: false, forceMount: true, className: "max-w-7xl w-[85vw] p-0 gap-0 overflow-hidden bg-white profile-component-dialog", style: { height: '85vh', display: 'flex', flexDirection: 'column' }, children: [jsx(DialogTitle, { className: "sr-only", children: t('modalsUserProfileModal.dialogTitle') }), jsx(DialogDescription, { className: "sr-only", children: t('modalsUserProfileModal.dialogDescription') }), [
267237
+ }, children: jsxs(DialogContent, { autoFocus: false, forceMount: true, className: "max-w-7xl w-[85vw] p-0 gap-0 overflow-hidden bg-white sm:rounded-2xl profile-component-dialog", style: { height: '85vh', display: 'flex', flexDirection: 'column' }, children: [jsx(DialogTitle, { className: "sr-only", children: t('modalsUserProfileModal.dialogTitle') }), jsx(DialogDescription, { className: "sr-only", children: t('modalsUserProfileModal.dialogDescription') }), [
265115
267238
  'basic',
265116
267239
  'social',
265117
267240
  'security',
@@ -265120,6 +267243,7 @@ function UserProfileModal({ isOpen, onClose, params, email, mainPlatformKey, sho
265120
267243
  'resume',
265121
267244
  'memory',
265122
267245
  'chatHistory',
267246
+ 'llmUsage',
265123
267247
  'advanced',
265124
267248
  ].includes(targetTab) && (jsx(Profile, { currentSPA: currentSPA, tenant: params.tenantKey, tenants: tenants, username: getUserName$1(), customization: {
265125
267249
  showMentorAIDisplayCheckbox: showMentorAIDisplayCheckbox,
@@ -265128,7 +267252,7 @@ function UserProfileModal({ isOpen, onClose, params, email, mainPlatformKey, sho
265128
267252
  showPlatformName: showPlatformName,
265129
267253
  useGravatarPicFallback: useGravatarPicFallback,
265130
267254
  showGradebookTab: showGradebookTab,
265131
- }, isAdmin: params.isAdmin, targetTab: targetTab, onClose: onClose, onAccountDeleted: onAccountDeleted, enableMemoryTab: enableMemoryTab, localLLMProps: localLLMProps })), [
267255
+ }, isAdmin: params.isAdmin, targetTab: targetTab, onClose: onClose, onAccountDeleted: onAccountDeleted, enableMemoryTab: enableMemoryTab, showLlmUsageTab: showLlmUsageTab, localLLMProps: localLLMProps })), [
265132
267256
  'organization',
265133
267257
  'management',
265134
267258
  'integrations',
@@ -267722,7 +269846,7 @@ function ConversationStarters$1({ onTemplateSelect, guidedPrompts = [], disabled
267722
269846
  }
267723
269847
  return (jsxs("div", { className: cn('w-full max-w-6xl mx-auto px-4', className), children: [jsx("h2", { className: "text-xl font-semibold text-gray-900 mb-6", children: t('welcomeChatConversationStarters.conversationStarters') }), jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5 gap-4", children: guidedPrompts.map((guidedPrompt, index) => {
267724
269848
  var _a;
267725
- return (jsx(Card, { className: cn('shadow-xs transition-all duration-200 bg-[#F5F8FF] border border-[#D0E0FF] h-full', disabled
269849
+ return (jsx(Card$1, { className: cn('shadow-xs transition-all duration-200 bg-[#F5F8FF] border border-[#D0E0FF] h-full', disabled
267726
269850
  ? 'opacity-50 cursor-not-allowed'
267727
269851
  : 'cursor-pointer hover:shadow-sm focus:shadow-md focus:outline-none focus:ring-2 focus:ring-gray-300 focus:ring-offset-2'), onClick: () => !disabled && onTemplateSelect(guidedPrompt.prompt), onKeyDown: (e) => {
267728
269852
  if (disabled)
@@ -276866,6 +278990,7 @@ const AGENT_HISTORY_TAB_LABELS = {
276866
278990
  selectPrompt: 'Select a conversation to view details.',
276867
278991
  aiAgent: 'AI Agent',
276868
278992
  noConversations: 'No conversations found',
278993
+ documentsChip: (count) => `Documents · ${count}`,
276869
278994
  },
276870
278995
  summary: {
276871
278996
  notAvailable: 'Summary not available',
@@ -276904,6 +279029,7 @@ function buildHistoryTabLabels(t) {
276904
279029
  selectPrompt: t('historyTabLabels.conversation.selectPrompt'),
276905
279030
  aiAgent: t('historyTabLabels.conversation.aiAgent'),
276906
279031
  noConversations: t('historyTabLabels.conversation.noConversations'),
279032
+ documentsChip: (count) => t('historyTabLabels.conversation.documentsChip', { count }),
276907
279033
  },
276908
279034
  summary: {
276909
279035
  notAvailable: t('historyTabLabels.summary.notAvailable'),
@@ -276923,6 +279049,8 @@ function resolveHistoryTabLabels(override, t) {
276923
279049
  }
276924
279050
 
276925
279051
  const ANONYMOUS_USERNAME$1 = 'anonymous';
279052
+ /** A turn's attachments in the shape the chat's file cards take. */
279053
+ const attachmentCards = (files) => (files !== null && files !== void 0 ? files : []).map((file) => ({ name: file.name, contentType: file.content_type, url: file.url }));
276926
279054
  function textTruncate$1(str, length, ending) {
276927
279055
  if (length == null) {
276928
279056
  length = 50;
@@ -276935,7 +279063,7 @@ function textTruncate$1(str, length, ending) {
276935
279063
  }
276936
279064
  return str;
276937
279065
  }
276938
- function AgentHistoryTab({ renderMessageContent, PaginationComponent, onExport, isExporting = false, labels: labelsOverride, }) {
279066
+ function AgentHistoryTab({ renderMessageContent, PaginationComponent, onExport, isExporting = false, labels: labelsOverride, currentSPA, }) {
276939
279067
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
276940
279068
  const t = useT();
276941
279069
  const { tenantKey, mentorId, username } = useAgentSettings();
@@ -276985,6 +279113,10 @@ function AgentHistoryTab({ renderMessageContent, PaginationComponent, onExport,
276985
279113
  // Use mentor summaries data for rating and tags
276986
279114
  const averageRating = (mentorSummaries === null || mentorSummaries === void 0 ? void 0 : mentorSummaries.rating) || 0;
276987
279115
  const topicTags = (mentorSummaries === null || mentorSummaries === void 0 ? void 0 : mentorSummaries.tags) || [];
279116
+ // Who the previewed conversation belongs to (email → full name → username → Anonymous).
279117
+ const identityOptions = { anonymousLabel: labels.conversation.anonymous };
279118
+ const selectedOwner = resolveUserIdentity(selectedConversation, identityOptions);
279119
+ const previewOwner = resolveUserIdentity(previewConversationContent, identityOptions);
276988
279120
  const renderAiContent = (content) => {
276989
279121
  if (renderMessageContent) {
276990
279122
  return renderMessageContent(content);
@@ -277016,7 +279148,10 @@ function AgentHistoryTab({ renderMessageContent, PaginationComponent, onExport,
277016
279148
  const conversation = _conversation;
277017
279149
  const messages = conversation.messages;
277018
279150
  const firstMessage = messages[0];
277019
- const name = conversation.lti_email || conversation.email || labels.conversation.anonymous;
279151
+ // Email → full name → username → Anonymous, decided in one place.
279152
+ const owner = resolveUserIdentity(conversation, {
279153
+ anonymousLabel: labels.conversation.anonymous,
279154
+ });
277020
279155
  const timeAgo = formatDistanceToNow(new Date(conversation.inserted_at), {
277021
279156
  addSuffix: true,
277022
279157
  });
@@ -277026,22 +279161,25 @@ function AgentHistoryTab({ renderMessageContent, PaginationComponent, onExport,
277026
279161
  const preview = (firstMessage === null || firstMessage === void 0 ? void 0 : firstMessage.ai)
277027
279162
  ? textTruncate$1(firstMessage.ai, 60)
277028
279163
  : labels.conversation.noResponse;
279164
+ // The admin list carries the turns inline (no server-side
279165
+ // rollups), so badge rows from the turns themselves.
279166
+ // The chip counts what the dialog lists: every document the
279167
+ // turns carry, as the backend reports them.
279168
+ const documents = conversationDocuments(messages);
279169
+ const rollup = summarizeTranscriptTurns(messages);
277029
279170
  return (jsxs("div", { className: cn('flex cursor-pointer items-center justify-between border-b p-4 last:border-b-0', (selectedConversation === null || selectedConversation === void 0 ? void 0 : selectedConversation.id) === conversation.id
277030
279171
  ? 'bg-gray-100'
277031
- : 'hover:bg-gray-50'), onClick: () => handleConversationClick(conversation), children: [jsxs("div", { className: "w-full", children: [jsxs("div", { className: "mb-2 flex items-center justify-between", children: [jsx("span", { className: "text-sm text-gray-600", children: timeAgo }), jsx("span", { className: "max-w-[100px] truncate text-sm text-gray-900 sm:max-w-[200px]", children: name })] }), jsx("div", { className: "font-medium text-gray-900", children: title }), jsx("p", { className: "line-clamp-1 text-sm text-gray-600", children: preview })] }), jsx("div", { className: "flex items-center gap-2 text-sm text-gray-600", children: jsx(ChevronRight, { className: "h-4 w-4" }) })] }, conversation.id));
277032
- }) }), totalPages > 1 && PaginationComponent && (jsx("div", { className: "my-2", children: jsx(PaginationComponent, { currentPage: currentPage, totalPages: totalPages, onPageChange: handlePageChange, disabled: isChatHistoryFetching || isChatHistoryLoading, disableNumberedButtons: isMobile }) }))] }), jsx("div", { className: "scrollbar-hide hidden max-h-[500px] flex-col overflow-y-auto rounded-lg border bg-white p-6 shadow-sm md:flex", tabIndex: 0, role: "region", "aria-label": t('tabsHistoryTab.conversationPreviewLabel'), children: selectedConversation && previewConversationContent ? (jsxs(Fragment$1, { children: [jsxs("div", { className: "mb-4", children: [jsx("h3", { className: "mb-1 text-base font-semibold text-gray-700", children: ((_m = selectedConversation.messages[0]) === null || _m === void 0 ? void 0 : _m.human) || labels.conversation.title }), jsx("span", { className: "text-sm text-gray-500", children: format$1(new Date(selectedConversation.inserted_at), "MMM dd, yyyy 'at' h:mm a") })] }), jsxs("div", { className: "space-y-6", children: [selectedConversation.messages.map((message, index) => {
277033
- var _a, _b;
277034
- return (jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-start gap-3", children: [jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-blue-100", children: jsx("span", { className: "text-sm font-medium text-blue-600", children: ((_b = (_a = (selectedConversation.lti_email || selectedConversation.email)) === null || _a === void 0 ? void 0 : _a.charAt(0)) === null || _b === void 0 ? void 0 : _b.toUpperCase()) || 'A' }) }), jsxs("div", { className: "flex-1", children: [jsx("div", { className: "font-medium text-gray-700", children: selectedConversation.lti_email ||
277035
- selectedConversation.email ||
277036
- labels.conversation.anonymous }), jsx("p", { className: "mt-1 text-sm whitespace-pre-line text-gray-500", children: message.human })] })] }), jsxs("div", { className: "flex items-start gap-3 border-t border-gray-200 pt-4", children: [jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-gray-100", children: jsx("span", { className: "text-sm font-medium text-gray-600", children: t('tabsHistoryTab.aiAvatarLabel') }) }), jsxs("div", { className: "flex-1", children: [jsx("div", { className: "font-medium text-gray-700", children: labels.conversation.aiAgent }), jsx("div", { className: "mt-1 text-sm text-gray-500", children: renderAiContent(message.ai) })] })] })] }, index));
277037
- }), (selectedConversation === null || selectedConversation === void 0 ? void 0 : selectedConversation.memory_tracked) && (jsxs("div", { className: "border-t border-gray-200 pt-4", children: [jsxs(Button$1, { variant: "outline", onClick: () => setShowConversationMemory(!showConversationMemory), className: "flex w-full items-center justify-center gap-2 border-gray-200 text-gray-700 hover:bg-gray-50", children: [jsx(Brain, { className: "h-4 w-4" }), showConversationMemory ? labels.memory.hide : labels.memory.show] }), showConversationMemory && (jsx("div", { className: "mt-4 space-y-6", children: isConversationMemoryLoading ? (jsx("div", { className: "flex items-center justify-center py-4", children: jsx(Spinner, {}) })) : ((_o = conversationMemory === null || conversationMemory === void 0 ? void 0 : conversationMemory.entries) === null || _o === void 0 ? void 0 : _o.length) ? (jsx(Fragment$1, { children: conversationMemory.entries.map((entry, index) => (jsxs("div", { children: [jsx("h4", { className: "mb-3 font-semibold text-gray-900", children: entry.key }), jsx("ul", { className: "space-y-2", children: jsx("li", { className: "flex items-start gap-2 text-sm text-gray-600", children: entry.value }) })] }, index))) })) : (jsx("div", { className: "text-sm text-gray-500", children: labels.memory.notAvailable })) }))] }))] })] })) : (jsx("div", { className: "flex h-full items-center justify-center text-gray-500", children: labels.conversation.selectPrompt })) })] }))] }) }), jsx(Dialog, { open: isConversationPreviewModalOpen, onOpenChange: setIsConversationPreviewModalOpen, children: jsxs(DialogContent, { className: "scrollbar-hide mx-auto max-h-[80vh] max-w-[90vw] overflow-x-hidden overflow-y-auto", children: [jsx(DialogHeader, { children: jsx(DialogTitle, { className: "text-lg font-semibold text-gray-900", children: previewConversationContent
279172
+ : 'hover:bg-gray-50'), onClick: () => handleConversationClick(conversation), children: [jsxs("div", { className: "w-full", children: [jsxs("div", { className: "mb-2 flex items-center justify-between", children: [jsx("span", { className: "text-sm text-gray-600", children: timeAgo }), jsx(UserProfileLink, { tenantKey: tenantKey, username: owner.profileUsername, display: owner.label, currentSPA: currentSPA, className: "max-w-[100px] text-sm text-gray-900 sm:max-w-[200px]" })] }), jsx("div", { className: "font-medium text-gray-900", children: title }), jsx("p", { className: "line-clamp-1 text-sm text-gray-600", children: preview }), (rollup.documentsCount > 0 || rollup.toolCallsCount > 0) && (jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-1.5", children: [rollup.documentsCount > 0 && (
279173
+ // The chat's documents dialog, as a chip: one
279174
+ // click shows what the conversation retrieved.
279175
+ jsx(RetrievedDocumentsButton, { documents: documents, label: labels.conversation.documentsChip(rollup.documentsCount), className: "h-6 gap-1 rounded-md border-transparent bg-blue-50 px-2 text-xs font-normal text-blue-700 hover:bg-blue-100 [&_svg]:h-3 [&_svg]:w-3" })), jsx(TranscriptRollupBadges, { documentsCount: 0, toolCallsCount: rollup.toolCallsCount })] }))] }), jsx("div", { className: "flex items-center gap-2 text-sm text-gray-600", children: jsx(ChevronRight, { className: "h-4 w-4" }) })] }, conversation.id));
279176
+ }) }), totalPages > 1 && PaginationComponent && (jsx("div", { className: "my-2", children: jsx(PaginationComponent, { currentPage: currentPage, totalPages: totalPages, onPageChange: handlePageChange, disabled: isChatHistoryFetching || isChatHistoryLoading, disableNumberedButtons: isMobile }) }))] }), jsx("div", { className: "scrollbar-hide hidden max-h-[500px] flex-col overflow-y-auto rounded-lg border bg-white p-6 shadow-sm md:flex", tabIndex: 0, role: "region", "aria-label": t('tabsHistoryTab.conversationPreviewLabel'), children: selectedConversation && previewConversationContent ? (jsxs(Fragment$1, { children: [jsxs("div", { className: "mb-4", children: [jsx("h3", { className: "mb-1 text-base font-semibold text-gray-700", children: ((_m = selectedConversation.messages[0]) === null || _m === void 0 ? void 0 : _m.human) || labels.conversation.title }), jsx("span", { className: "text-sm text-gray-500", children: format$1(new Date(selectedConversation.inserted_at), "MMM dd, yyyy 'at' h:mm a") }), jsx(RetrievedDocumentsButton, { sessionId: selectedConversation.id, documents: conversationDocuments(selectedConversation.messages).length > 0
279177
+ ? conversationDocuments(selectedConversation.messages)
279178
+ : undefined, className: "mt-2" })] }), jsxs("div", { className: "space-y-6", children: [selectedConversation.messages.map((message, index) => (jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-start gap-3", children: [jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-blue-100", children: jsx("span", { className: "text-sm font-medium text-blue-600", children: selectedOwner.initial }) }), jsxs("div", { className: "flex-1", children: [jsx("div", { className: "font-medium text-gray-700", children: jsx(UserProfileLink, { tenantKey: tenantKey, username: selectedOwner.profileUsername, display: selectedOwner.label, currentSPA: currentSPA }) }), jsx("p", { className: "mt-1 text-sm whitespace-pre-line text-gray-500", children: message.human }), jsx(TranscriptFileCards, { files: attachmentCards(message.human_files) })] })] }), jsxs("div", { className: "flex items-start gap-3 border-t border-gray-200 pt-4", children: [jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-gray-100", children: jsx("span", { className: "text-sm font-medium text-gray-600", children: t('tabsHistoryTab.aiAvatarLabel') }) }), jsxs("div", { className: "flex-1", children: [jsx("div", { className: "font-medium text-gray-700", children: labels.conversation.aiAgent }), jsx("div", { className: "mt-1 text-sm text-gray-500", children: renderAiContent(message.ai) }), jsx(TranscriptFileCards, { files: attachmentCards(message.ai_files) }), jsx(TranscriptTurnDetails, { turn: message })] })] })] }, index))), (selectedConversation === null || selectedConversation === void 0 ? void 0 : selectedConversation.memory_tracked) && (jsxs("div", { className: "border-t border-gray-200 pt-4", children: [jsxs(Button$1, { variant: "outline", onClick: () => setShowConversationMemory(!showConversationMemory), className: "flex w-full items-center justify-center gap-2 border-gray-200 text-gray-700 hover:bg-gray-50", children: [jsx(Brain, { className: "h-4 w-4" }), showConversationMemory ? labels.memory.hide : labels.memory.show] }), showConversationMemory && (jsx("div", { className: "mt-4 space-y-6", children: isConversationMemoryLoading ? (jsx("div", { className: "flex items-center justify-center py-4", children: jsx(Spinner, {}) })) : ((_o = conversationMemory === null || conversationMemory === void 0 ? void 0 : conversationMemory.entries) === null || _o === void 0 ? void 0 : _o.length) ? (jsx(Fragment$1, { children: conversationMemory.entries.map((entry, index) => (jsxs("div", { children: [jsx("h4", { className: "mb-3 font-semibold text-gray-900", children: entry.key }), jsx("ul", { className: "space-y-2", children: jsx("li", { className: "flex items-start gap-2 text-sm text-gray-600", children: entry.value }) })] }, index))) })) : (jsx("div", { className: "text-sm text-gray-500", children: labels.memory.notAvailable })) }))] }))] })] })) : (jsx("div", { className: "flex h-full items-center justify-center text-gray-500", children: labels.conversation.selectPrompt })) })] }))] }) }), jsx(Dialog, { open: isConversationPreviewModalOpen, onOpenChange: setIsConversationPreviewModalOpen, children: jsxs(DialogContent, { className: "scrollbar-hide mx-auto max-h-[80vh] max-w-[90vw] overflow-x-hidden overflow-y-auto", children: [jsx(DialogHeader, { children: jsx(DialogTitle, { className: "text-lg font-semibold text-gray-900", children: previewConversationContent
277038
279179
  ? ((_p = previewConversationContent.messages[0]) === null || _p === void 0 ? void 0 : _p.human) || labels.conversation.title
277039
- : labels.conversation.title }) }), jsx("div", { className: "mx-auto max-w-full overflow-x-hidden", children: previewConversationContent && (jsxs("div", { className: "scrollbar-hide space-y-4 overflow-y-auto", children: [jsx("div", { className: "text-sm text-gray-500", children: format$1(new Date(previewConversationContent.inserted_at), "MMM dd, yyyy 'at' h:mm a") }), previewConversationContent.messages.map((message, index) => {
277040
- var _a, _b;
277041
- return (jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-start gap-3", children: [jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-blue-100", children: jsx("span", { className: "text-sm font-medium text-blue-600", children: ((_b = (_a = (previewConversationContent.lti_email || previewConversationContent.email)) === null || _a === void 0 ? void 0 : _a.charAt(0)) === null || _b === void 0 ? void 0 : _b.toUpperCase()) || 'A' }) }), jsxs("div", { className: "overflow-x-hidden", children: [jsx("div", { className: "truncate font-medium text-gray-900", children: previewConversationContent.lti_email ||
277042
- previewConversationContent.email ||
277043
- labels.conversation.anonymous }), jsx("p", { className: "mt-1 text-sm whitespace-pre-line text-gray-900", children: message.human })] })] }), jsxs("div", { className: "flex items-start gap-3 border-t border-gray-200 pt-4", children: [jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-gray-100", children: jsx("span", { className: "text-sm font-medium text-gray-600", children: t('tabsHistoryTab.aiAvatarLabel') }) }), jsxs("div", { className: "overflow-x-hidden", children: [jsx("div", { className: "font-medium text-gray-900", children: labels.conversation.aiAgent }), jsx("div", { className: "mt-1 text-sm text-gray-900", children: renderAiContent(message.ai) })] })] })] }, index));
277044
- })] })) })] }) })] }));
279180
+ : labels.conversation.title }) }), jsx("div", { className: "mx-auto max-w-full overflow-x-hidden", children: previewConversationContent && (jsxs("div", { className: "scrollbar-hide space-y-4 overflow-y-auto", children: [jsx("div", { className: "text-sm text-gray-500", children: format$1(new Date(previewConversationContent.inserted_at), "MMM dd, yyyy 'at' h:mm a") }), jsx(RetrievedDocumentsButton, { sessionId: previewConversationContent.id, documents: conversationDocuments(previewConversationContent.messages).length > 0
279181
+ ? conversationDocuments(previewConversationContent.messages)
279182
+ : undefined }), previewConversationContent.messages.map((message, index) => (jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-start gap-3", children: [jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-blue-100", children: jsx("span", { className: "text-sm font-medium text-blue-600", children: previewOwner.initial }) }), jsxs("div", { className: "overflow-x-hidden", children: [jsx("div", { className: "truncate font-medium text-gray-900", children: jsx(UserProfileLink, { tenantKey: tenantKey, username: previewOwner.profileUsername, display: previewOwner.label, currentSPA: currentSPA }) }), jsx("p", { className: "mt-1 text-sm whitespace-pre-line text-gray-900", children: message.human }), jsx(TranscriptFileCards, { files: attachmentCards(message.human_files) })] })] }), jsxs("div", { className: "flex items-start gap-3 border-t border-gray-200 pt-4", children: [jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-gray-100", children: jsx("span", { className: "text-sm font-medium text-gray-600", children: t('tabsHistoryTab.aiAvatarLabel') }) }), jsxs("div", { className: "overflow-x-hidden", children: [jsx("div", { className: "font-medium text-gray-900", children: labels.conversation.aiAgent }), jsx("div", { className: "mt-1 text-sm text-gray-900", children: renderAiContent(message.ai) }), jsx(TranscriptFileCards, { files: attachmentCards(message.ai_files) }), jsx(TranscriptTurnDetails, { turn: message })] })] })] }, index)))] })) })] }) })] }));
277045
279183
  }
277046
279184
 
277047
279185
  const ANONYMOUS_USERNAME = 'anonymous';
@@ -280027,7 +282165,7 @@ function AgentEmbedTab({ labels: labelsProp, urls, CopyCodeBlock, visibilityOpti
280027
282165
  ? jsValidation.warnings.length > 0
280028
282166
  ? 'bg-blue-50 text-blue-700'
280029
282167
  : 'bg-green-50 text-green-700'
280030
- : 'bg-red-50 text-red-700'), children: jsValidation.isValid ? (jsValidation.warnings.length > 0 ? (jsxs(Fragment$1, { children: [jsx(TriangleAlert, { className: "h-3.5 w-3.5" }), t('tabsEmbedTab.warnings')] })) : (jsxs(Fragment$1, { children: [jsx(Check, { className: "h-3.5 w-3.5" }), t('tabsEmbedTab.valid')] }))) : (jsxs(Fragment$1, { children: [jsx(CircleAlert, { className: "h-3.5 w-3.5" }), t('tabsEmbedTab.invalid')] })) })) })] }), !jsValidation.isValid && (jsxs("div", { id: "js-errors", className: "rounded-lg border border-red-100 bg-red-50 p-3", role: "alert", children: [jsx("p", { className: "mb-1 text-sm font-medium text-red-800", children: t('tabsEmbedTab.jsValidationErrors') }), jsx("ul", { className: "list-inside list-disc space-y-0.5 text-sm text-red-700", children: jsValidation.errors.map((error, index) => (jsx("li", { children: error }, index))) })] })), jsValidation.isValid && jsValidation.warnings.length > 0 && (jsxs("div", { id: "js-warnings", className: "rounded-lg border border-blue-100 bg-blue-50 p-3", role: "status", children: [jsx("p", { className: "mb-1 text-sm font-medium text-blue-800", children: t('tabsEmbedTab.warningsHeading') }), jsx("ul", { className: "list-inside list-disc space-y-0.5 text-sm text-blue-700", children: jsValidation.warnings.map((warning, index) => (jsx("li", { children: warning }, index))) })] })), jsxs("div", { className: "flex items-center justify-between pt-2", children: [jsx("p", { className: "text-xs text-gray-500", children: t('tabsEmbedTab.jsChangesHint') }), jsxs("div", { className: "flex items-center gap-2", children: [hasJsChanges && (jsx(Button$1, { type: "button", variant: "outline", size: "sm", onClick: handleDiscardJs, disabled: isAdvancedDisabled, "aria-label": labels.actions.discard, children: labels.actions.discard })), jsx(Button$1, { type: "button", size: "sm", onClick: handleSaveJs, disabled: !canSaveJs, className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", "aria-label": `${labels.actions.save} ${labels.advancedJs.title.toLowerCase()}`, children: isSavingAdvanced ? labels.actions.saving : labels.actions.save })] })] })] })) }))] }), jsx(form.Subscribe, { selector: (formState) => [formState.values.icon_selection], children: ([iconSelection]) => (jsxs(Fragment$1, { children: [jsx(form.Field, { name: "icon_selection", children: (field) => (jsxs("div", { className: "space-y-2", children: [jsx("h3", { className: "text-sm font-medium text-[#646464]", children: labels.fields.iconSelection }), jsxs(Select$1, { defaultValue: field.state.value, onValueChange: (value) => field.handleChange(value), disabled: form.state.isSubmitting, children: [jsx(SelectTrigger, { className: "text-[#646464]", "aria-label": t('tabsEmbedTab.selectEmbedModeAriaLabel'), children: jsx(SelectValue, { placeholder: t('tabsEmbedTab.selectModePlaceholder'), className: "text-[#646464]" }) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "default", className: "text-[#646464]", children: t('tabsEmbedTab.iconSelectionDefault') }), jsx(SelectItem, { value: "custom", className: "text-[#646464]", children: t('tabsEmbedTab.iconSelectionCustom') })] })] })] })) }), iconSelection === 'custom' && (jsxs("div", { className: "flex w-full flex-col items-center gap-4", children: [jsx(Button$1, { type: "button", variant: "outline", className: "w-full bg-gray-50 text-gray-700 hover:bg-gray-100", onClick: () => setFocusEditCustomFloatingBubble(true), children: labels.iconEditor.title }), jsxs(Card, { children: [jsx(CardHeader, { className: "mb-0 pt-4 pb-0", children: jsx(CardTitle, { className: "text-sm font-medium text-[#646464]", children: t('tabsEmbedTab.livePreview') }) }), jsx(CardContent, { className: "mt-0", children: jsx(InteractiveBubbleConfigDisplay, { customFloatingBubbleConfig: customFloatingBubbleConfig, handleFloatingBubbleImageError: handleFloatingBubbleImageError }) })] })] }))] })) }), jsx("hr", { className: "my-4 border-t border-gray-200" }), jsx(form.Field, { name: "mode", children: (field) => (jsxs("div", { className: "space-y-2", children: [jsx("h3", { className: "text-sm font-medium text-[#646464]", children: labels.fields.modeSelection }), jsxs(Select$1, { defaultValue: field.state.value, onValueChange: (value) => field.handleChange(value), disabled: form.state.isSubmitting, children: [jsx(SelectTrigger, { className: "text-[#646464]", "aria-label": t('tabsEmbedTab.selectEmbedModeAriaLabel'), children: jsx(SelectValue, { placeholder: t('tabsEmbedTab.selectModePlaceholder'), className: "text-[#646464]" }) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "default", className: "text-[#646464]", children: t('tabsEmbedTab.modeDefault') }), jsx(SelectItem, { value: "advanced", className: "text-[#646464]", children: t('tabsEmbedTab.modeAdvanced') })] })] })] })) }), jsx(form.Field, { name: "starter_prompts", children: (field) => (jsxs("div", { className: "space-y-2", children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx("h3", { className: "text-sm font-medium text-[#646464]", children: labels.fields.starterPrompts.label }), jsx(TooltipProvider, { children: jsxs(Tooltip$1, { children: [jsx(TooltipTrigger, { "aria-label": t('tabsEmbedTab.moreInfoStarterPrompts'), children: jsx(Info$3, { className: "h-4 w-4 text-gray-400" }) }), jsx(TooltipContent, { className: "ibl-tooltip-content", children: jsx("p", { children: labels.fields.starterPrompts.tooltip }) })] }) })] }), jsxs(Select$1, { value: field.state.value, onValueChange: (value) => field.handleChange(value), disabled: form.state.isSubmitting, children: [jsx(SelectTrigger, { className: "text-[#646464]", "aria-label": t('tabsEmbedTab.selectStarterPromptsAriaLabel'), children: jsx(SelectValue, { placeholder: t('tabsEmbedTab.selectStarterPromptsPlaceholder'), className: "text-[#646464]" }) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "guided_prompt", className: "text-[#646464]", children: t('tabsEmbedTab.guidedPrompts') }), jsx(SelectItem, { value: "suggested_prompt", className: "text-[#646464]", children: t('tabsEmbedTab.suggestedPrompts') })] })] })] })) }), jsx(WithFormPermissions, { name: "mentor_visibility",
282168
+ : 'bg-red-50 text-red-700'), children: jsValidation.isValid ? (jsValidation.warnings.length > 0 ? (jsxs(Fragment$1, { children: [jsx(TriangleAlert, { className: "h-3.5 w-3.5" }), t('tabsEmbedTab.warnings')] })) : (jsxs(Fragment$1, { children: [jsx(Check, { className: "h-3.5 w-3.5" }), t('tabsEmbedTab.valid')] }))) : (jsxs(Fragment$1, { children: [jsx(CircleAlert, { className: "h-3.5 w-3.5" }), t('tabsEmbedTab.invalid')] })) })) })] }), !jsValidation.isValid && (jsxs("div", { id: "js-errors", className: "rounded-lg border border-red-100 bg-red-50 p-3", role: "alert", children: [jsx("p", { className: "mb-1 text-sm font-medium text-red-800", children: t('tabsEmbedTab.jsValidationErrors') }), jsx("ul", { className: "list-inside list-disc space-y-0.5 text-sm text-red-700", children: jsValidation.errors.map((error, index) => (jsx("li", { children: error }, index))) })] })), jsValidation.isValid && jsValidation.warnings.length > 0 && (jsxs("div", { id: "js-warnings", className: "rounded-lg border border-blue-100 bg-blue-50 p-3", role: "status", children: [jsx("p", { className: "mb-1 text-sm font-medium text-blue-800", children: t('tabsEmbedTab.warningsHeading') }), jsx("ul", { className: "list-inside list-disc space-y-0.5 text-sm text-blue-700", children: jsValidation.warnings.map((warning, index) => (jsx("li", { children: warning }, index))) })] })), jsxs("div", { className: "flex items-center justify-between pt-2", children: [jsx("p", { className: "text-xs text-gray-500", children: t('tabsEmbedTab.jsChangesHint') }), jsxs("div", { className: "flex items-center gap-2", children: [hasJsChanges && (jsx(Button$1, { type: "button", variant: "outline", size: "sm", onClick: handleDiscardJs, disabled: isAdvancedDisabled, "aria-label": labels.actions.discard, children: labels.actions.discard })), jsx(Button$1, { type: "button", size: "sm", onClick: handleSaveJs, disabled: !canSaveJs, className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", "aria-label": `${labels.actions.save} ${labels.advancedJs.title.toLowerCase()}`, children: isSavingAdvanced ? labels.actions.saving : labels.actions.save })] })] })] })) }))] }), jsx(form.Subscribe, { selector: (formState) => [formState.values.icon_selection], children: ([iconSelection]) => (jsxs(Fragment$1, { children: [jsx(form.Field, { name: "icon_selection", children: (field) => (jsxs("div", { className: "space-y-2", children: [jsx("h3", { className: "text-sm font-medium text-[#646464]", children: labels.fields.iconSelection }), jsxs(Select$1, { defaultValue: field.state.value, onValueChange: (value) => field.handleChange(value), disabled: form.state.isSubmitting, children: [jsx(SelectTrigger, { className: "text-[#646464]", "aria-label": t('tabsEmbedTab.selectEmbedModeAriaLabel'), children: jsx(SelectValue, { placeholder: t('tabsEmbedTab.selectModePlaceholder'), className: "text-[#646464]" }) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "default", className: "text-[#646464]", children: t('tabsEmbedTab.iconSelectionDefault') }), jsx(SelectItem, { value: "custom", className: "text-[#646464]", children: t('tabsEmbedTab.iconSelectionCustom') })] })] })] })) }), iconSelection === 'custom' && (jsxs("div", { className: "flex w-full flex-col items-center gap-4", children: [jsx(Button$1, { type: "button", variant: "outline", className: "w-full bg-gray-50 text-gray-700 hover:bg-gray-100", onClick: () => setFocusEditCustomFloatingBubble(true), children: labels.iconEditor.title }), jsxs(Card$1, { children: [jsx(CardHeader, { className: "mb-0 pt-4 pb-0", children: jsx(CardTitle, { className: "text-sm font-medium text-[#646464]", children: t('tabsEmbedTab.livePreview') }) }), jsx(CardContent, { className: "mt-0", children: jsx(InteractiveBubbleConfigDisplay, { customFloatingBubbleConfig: customFloatingBubbleConfig, handleFloatingBubbleImageError: handleFloatingBubbleImageError }) })] })] }))] })) }), jsx("hr", { className: "my-4 border-t border-gray-200" }), jsx(form.Field, { name: "mode", children: (field) => (jsxs("div", { className: "space-y-2", children: [jsx("h3", { className: "text-sm font-medium text-[#646464]", children: labels.fields.modeSelection }), jsxs(Select$1, { defaultValue: field.state.value, onValueChange: (value) => field.handleChange(value), disabled: form.state.isSubmitting, children: [jsx(SelectTrigger, { className: "text-[#646464]", "aria-label": t('tabsEmbedTab.selectEmbedModeAriaLabel'), children: jsx(SelectValue, { placeholder: t('tabsEmbedTab.selectModePlaceholder'), className: "text-[#646464]" }) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "default", className: "text-[#646464]", children: t('tabsEmbedTab.modeDefault') }), jsx(SelectItem, { value: "advanced", className: "text-[#646464]", children: t('tabsEmbedTab.modeAdvanced') })] })] })] })) }), jsx(form.Field, { name: "starter_prompts", children: (field) => (jsxs("div", { className: "space-y-2", children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx("h3", { className: "text-sm font-medium text-[#646464]", children: labels.fields.starterPrompts.label }), jsx(TooltipProvider, { children: jsxs(Tooltip$1, { children: [jsx(TooltipTrigger, { "aria-label": t('tabsEmbedTab.moreInfoStarterPrompts'), children: jsx(Info$3, { className: "h-4 w-4 text-gray-400" }) }), jsx(TooltipContent, { className: "ibl-tooltip-content", children: jsx("p", { children: labels.fields.starterPrompts.tooltip }) })] }) })] }), jsxs(Select$1, { value: field.state.value, onValueChange: (value) => field.handleChange(value), disabled: form.state.isSubmitting, children: [jsx(SelectTrigger, { className: "text-[#646464]", "aria-label": t('tabsEmbedTab.selectStarterPromptsAriaLabel'), children: jsx(SelectValue, { placeholder: t('tabsEmbedTab.selectStarterPromptsPlaceholder'), className: "text-[#646464]" }) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "guided_prompt", className: "text-[#646464]", children: t('tabsEmbedTab.guidedPrompts') }), jsx(SelectItem, { value: "suggested_prompt", className: "text-[#646464]", children: t('tabsEmbedTab.suggestedPrompts') })] })] })] })) }), jsx(WithFormPermissions, { name: "mentor_visibility",
280031
282169
  // @ts-ignore
280032
282170
  permissions: (_b = mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.permissions) === null || _b === void 0 ? void 0 : _b.field, children: ({ disabled }) => (jsx(form.Field, { name: "mentor_visibility", children: (field) => (jsxs("div", { className: "space-y-2", children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx(Label$1, { className: "text-sm font-medium text-[#646464]", children: labels.fields.whoCanView.label }), jsx(TooltipProvider, { children: jsxs(Tooltip$1, { children: [jsx(TooltipTrigger, { "aria-label": t('tabsEmbedTab.moreInfoVisibility'), children: jsx(Info$3, { className: "h-4 w-4 text-gray-400" }) }), jsx(TooltipContent, { className: "ibl-tooltip-content", children: jsx("p", { children: labels.fields.whoCanView.tooltip }) })] }) })] }), jsxs(Select$1, { value: typeof field.state.value === 'string' ? field.state.value : '', onValueChange: (value) => field.handleChange(value), disabled: form.state.isSubmitting || disabled, children: [jsx(SelectTrigger, { className: "text-[#646464]", "aria-label": t('tabsEmbedTab.selectWhoCanViewAriaLabel'), children: jsx(SelectValue, { placeholder: t('tabsEmbedTab.selectWhoCanViewPlaceholder'), className: "text-[#646464]" }) }), jsx(SelectContent, { children: visibilityOptions.map((visibility) => (jsx(SelectItem, { value: visibility.value, className: "text-[#646464]", children: visibility.label }, visibility.value))) })] })] })) })) }), jsx(WithFormPermissions, { name: "allow_anonymous",
280033
282171
  // @ts-ignore
@@ -280058,7 +282196,7 @@ function AgentEmbedTab({ labels: labelsProp, urls, CopyCodeBlock, visibilityOpti
280058
282196
  minHeight: '580px',
280059
282197
  width: '100%',
280060
282198
  minWidth: '280px',
280061
- }, frameBorder: "0" })) }) })] }), focusEditCustomFloatingBubble && (jsx(Dialog, { open: focusEditCustomFloatingBubble, onOpenChange: (open) => setFocusEditCustomFloatingBubble(open), children: jsxs(DialogContent, { className: "max-h-[80vh] max-w-[600px] overflow-y-auto", children: [jsxs(DialogHeader, { className: "mb-1", children: [jsx(DialogTitle, { className: "ibl-dialog-title", children: labels.iconEditor.title }), jsx("p", { className: "text-xs text-gray-600", children: labels.iconEditor.description })] }), jsxs(Tabs, { defaultValue: "appearance", className: "w-full", children: [jsxs(TabsList, { className: "mb-4 grid h-10 w-full grid-cols-3", children: [jsxs(TabsTrigger, { value: "appearance", className: "flex items-center gap-2", children: [jsx(Palette, { className: "h-4 w-4" }), t('tabsEmbedTab.appearanceTab')] }), jsxs(TabsTrigger, { value: "position", className: "flex items-center gap-2", children: [jsx(Settings$1, { className: "h-4 w-4" }), t('tabsEmbedTab.positionTab')] }), jsxs(TabsTrigger, { value: "content", className: "flex items-center gap-2", children: [jsx(MessageCircle, { className: "h-4 w-4" }), t('tabsEmbedTab.contentTab')] })] }), jsx(TabsContent, { value: "appearance", className: "space-y-6", children: jsxs(Card, { children: [jsx(CardHeader, { className: "mb-0 px-4 py-3", children: jsx(CardTitle, { className: "text-sm font-medium text-gray-600", children: t('tabsEmbedTab.visualStyling') }) }), jsx(CardContent, { className: "space-y-6 pt-2", children: jsxs("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2", children: [jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "backgroundColor", className: "text-sm text-gray-600", children: t('tabsEmbedTab.backgroundColor') }), jsxs("div", { className: "mt-2 flex gap-2", children: [jsx(Input, { id: "backgroundColor", type: "color", value: customFloatingBubbleConfig.backgroundColor, onChange: (e) => updateConfig('backgroundColor', e.target.value), className: "h-10 w-16 rounded border p-1" }), jsx(Input, { value: customFloatingBubbleConfig.backgroundColor, onChange: (e) => updateConfig('backgroundColor', e.target.value), placeholder: "#3b82f6", className: "flex-1" })] })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "textColor", className: "text-sm text-gray-600", children: t('tabsEmbedTab.titleTextColor') }), jsxs("div", { className: "mt-2 flex gap-2", children: [jsx(Input, { id: "textColor", type: "color", value: customFloatingBubbleConfig.textColor, onChange: (e) => updateConfig('textColor', e.target.value), className: "h-10 w-16 rounded border p-1" }), jsx(Input, { value: customFloatingBubbleConfig.textColor, onChange: (e) => updateConfig('textColor', e.target.value), placeholder: "#ffffff", className: "flex-1" })] })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "subtitleTextColor", className: "text-sm text-gray-600", children: t('tabsEmbedTab.subtitleTextColor') }), jsxs("div", { className: "mt-2 flex gap-2", children: [jsx(Input, { id: "subtitleTextColor", type: "color", value: customFloatingBubbleConfig.subtitleTextColor, onChange: (e) => updateConfig('subtitleTextColor', e.target.value), className: "h-10 w-16 rounded border p-1" }), jsx(Input, { value: customFloatingBubbleConfig.subtitleTextColor, onChange: (e) => updateConfig('subtitleTextColor', e.target.value), placeholder: "#e5e7eb", className: "flex-1" })] })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "borderRadius", className: "text-sm text-gray-600", children: t('tabsEmbedTab.borderRadius') }), jsx("div", { className: "mt-2 flex gap-2", children: jsx(Input, { value: customFloatingBubbleConfig.borderRadius, onChange: (e) => updateConfig('borderRadius', e.target.value), placeholder: "16", type: "range", min: 0, max: 100, step: 1, className: "flex-1", id: "borderRadius" }) })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "imageSize", className: "text-sm text-gray-600", children: t('tabsEmbedTab.imageSize') }), jsx("div", { className: "mt-2 flex gap-2", children: jsx(Input, { value: customFloatingBubbleConfig.imageSize, onChange: (e) => updateConfig('imageSize', e.target.value), placeholder: "16", type: "range", min: 0, max: 100, step: 1, className: "flex-1" }) })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "fontSize", className: "text-sm text-gray-600", children: t('tabsEmbedTab.titleFontSize') }), jsxs("div", { className: "mt-2 flex gap-2", children: [jsx(Input, { id: "fontSize", value: customFloatingBubbleConfig.fontSize, onChange: (e) => updateConfig('fontSize', e.target.value), placeholder: "16", type: "range", min: 10, max: 24, step: 1, className: "flex-1" }), jsxs("span", { className: "flex min-w-[30px] items-center text-xs text-gray-500", children: [customFloatingBubbleConfig.fontSize, "px"] })] })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "subtitleFontSize", className: "text-sm text-gray-600", children: t('tabsEmbedTab.subtitleFontSize') }), jsxs("div", { className: "mt-2 flex gap-2", children: [jsx(Input, { id: "subtitleFontSize", value: customFloatingBubbleConfig.subtitleFontSize, onChange: (e) => updateConfig('subtitleFontSize', e.target.value), placeholder: "12", type: "range", min: 8, max: 20, step: 1, className: "flex-1" }), jsxs("span", { className: "flex min-w-[30px] items-center text-xs text-gray-500", children: [customFloatingBubbleConfig.subtitleFontSize, "px"] })] })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "shadow", className: "text-sm text-gray-600", children: t('tabsEmbedTab.useShadow') }), jsx("div", { className: "mt-2", children: jsxs(Select$1, { value: customFloatingBubbleConfig.shadow ? '1' : '0', onValueChange: (value) => updateConfig('shadow', value === '1'), children: [jsx(SelectTrigger, { children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: '1', children: t('tabsEmbedTab.yes') }), jsx(SelectItem, { value: '0', children: t('tabsEmbedTab.no') })] })] }) })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "padding", className: "text-sm text-gray-600", children: t('tabsEmbedTab.padding') }), jsx("div", { className: "mt-2 flex gap-2", children: jsx(Input, { value: customFloatingBubbleConfig.padding, onChange: (e) => updateConfig('padding', e.target.value), placeholder: "1", type: "range", min: 0, max: 50, step: 1, className: "flex-1" }) })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "strokeWidth", className: "text-sm text-gray-600", children: t('tabsEmbedTab.strokeThickness') }), jsx("div", { className: "mt-2 flex gap-2", children: jsx(Input, { value: customFloatingBubbleConfig.strokeWidth, onChange: (e) => updateConfig('strokeWidth', e.target.value), placeholder: "1", type: "range", min: 0, max: 10, step: 1, className: "flex-1" }) })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "strokeColor", className: "text-sm text-gray-600", children: t('tabsEmbedTab.strokeColor') }), jsxs("div", { className: "mt-2 flex gap-2", children: [jsx(Input, { id: "strokeColor", type: "color", value: customFloatingBubbleConfig.strokeColor, onChange: (e) => updateConfig('strokeColor', e.target.value), className: "h-10 w-16 rounded border p-1" }), jsx(Input, { value: customFloatingBubbleConfig.strokeColor, onChange: (e) => updateConfig('strokeColor', e.target.value), placeholder: "#3b82f6", className: "flex-1" })] })] })] }) })] }) }), jsx(TabsContent, { value: "position", className: "space-y-6", children: jsxs(Card, { children: [jsx(CardHeader, { className: "mb-0 px-4 py-3", children: jsx(CardTitle, { className: "text-sm font-medium text-gray-600", children: t('tabsEmbedTab.positionLayout') }) }), jsx(CardContent, { className: "space-y-6 pt-0", children: jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "position", className: "text-sm text-gray-600", children: t('tabsEmbedTab.screenPosition') }), jsx("div", { className: "mt-2", children: jsxs(Select$1, { value: customFloatingBubbleConfig.position, onValueChange: (value) => updateConfig('position', value), children: [jsx(SelectTrigger, { children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "bottom-right", children: t('tabsEmbedTab.bottomRight') }), jsx(SelectItem, { value: "bottom-left", children: t('tabsEmbedTab.bottomLeft') }), jsx(SelectItem, { value: "top-right", children: t('tabsEmbedTab.topRight') }), jsx(SelectItem, { value: "top-left", children: t('tabsEmbedTab.topLeft') })] })] }) })] }) })] }) }), jsx(TabsContent, { value: "content", className: "space-y-6", children: jsxs(Card, { children: [jsx(CardHeader, { className: "mb-0 px-4 py-3", children: jsx(CardTitle, { className: "text-sm font-medium text-gray-600", children: t('tabsEmbedTab.textContent') }) }), jsxs(CardContent, { className: "space-y-4 pt-0", children: [jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "title", className: "text-sm text-gray-600", children: t('tabsEmbedTab.titleLabel') }), jsx(Input, { id: "title", value: customFloatingBubbleConfig.title, onChange: (e) => updateConfig('title', e.target.value), placeholder: "AI-powered assistant", className: "mt-2" })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "subtitle", className: "text-sm text-gray-600", children: t('tabsEmbedTab.subtitleText') }), jsx(Input, { id: "subtitle", value: customFloatingBubbleConfig.subtitle, onChange: (e) => updateConfig('subtitle', e.target.value), placeholder: "Created with AI", className: "mt-2" })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "iconImage", className: "text-sm text-gray-600", children: t('tabsEmbedTab.iconImage') }), jsxs("div", { className: "mt-2 space-y-1", children: [jsx(Input, { id: "iconImage", type: "file", accept: "image/*", onChange: (e) => {
282199
+ }, frameBorder: "0" })) }) })] }), focusEditCustomFloatingBubble && (jsx(Dialog, { open: focusEditCustomFloatingBubble, onOpenChange: (open) => setFocusEditCustomFloatingBubble(open), children: jsxs(DialogContent, { className: "max-h-[80vh] max-w-[600px] overflow-y-auto", children: [jsxs(DialogHeader, { className: "mb-1", children: [jsx(DialogTitle, { className: "ibl-dialog-title", children: labels.iconEditor.title }), jsx("p", { className: "text-xs text-gray-600", children: labels.iconEditor.description })] }), jsxs(Tabs, { defaultValue: "appearance", className: "w-full", children: [jsxs(TabsList, { className: "mb-4 grid h-10 w-full grid-cols-3", children: [jsxs(TabsTrigger, { value: "appearance", className: "flex items-center gap-2", children: [jsx(Palette, { className: "h-4 w-4" }), t('tabsEmbedTab.appearanceTab')] }), jsxs(TabsTrigger, { value: "position", className: "flex items-center gap-2", children: [jsx(Settings$1, { className: "h-4 w-4" }), t('tabsEmbedTab.positionTab')] }), jsxs(TabsTrigger, { value: "content", className: "flex items-center gap-2", children: [jsx(MessageCircle, { className: "h-4 w-4" }), t('tabsEmbedTab.contentTab')] })] }), jsx(TabsContent, { value: "appearance", className: "space-y-6", children: jsxs(Card$1, { children: [jsx(CardHeader, { className: "mb-0 px-4 py-3", children: jsx(CardTitle, { className: "text-sm font-medium text-gray-600", children: t('tabsEmbedTab.visualStyling') }) }), jsx(CardContent, { className: "space-y-6 pt-2", children: jsxs("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2", children: [jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "backgroundColor", className: "text-sm text-gray-600", children: t('tabsEmbedTab.backgroundColor') }), jsxs("div", { className: "mt-2 flex gap-2", children: [jsx(Input, { id: "backgroundColor", type: "color", value: customFloatingBubbleConfig.backgroundColor, onChange: (e) => updateConfig('backgroundColor', e.target.value), className: "h-10 w-16 rounded border p-1" }), jsx(Input, { value: customFloatingBubbleConfig.backgroundColor, onChange: (e) => updateConfig('backgroundColor', e.target.value), placeholder: "#3b82f6", className: "flex-1" })] })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "textColor", className: "text-sm text-gray-600", children: t('tabsEmbedTab.titleTextColor') }), jsxs("div", { className: "mt-2 flex gap-2", children: [jsx(Input, { id: "textColor", type: "color", value: customFloatingBubbleConfig.textColor, onChange: (e) => updateConfig('textColor', e.target.value), className: "h-10 w-16 rounded border p-1" }), jsx(Input, { value: customFloatingBubbleConfig.textColor, onChange: (e) => updateConfig('textColor', e.target.value), placeholder: "#ffffff", className: "flex-1" })] })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "subtitleTextColor", className: "text-sm text-gray-600", children: t('tabsEmbedTab.subtitleTextColor') }), jsxs("div", { className: "mt-2 flex gap-2", children: [jsx(Input, { id: "subtitleTextColor", type: "color", value: customFloatingBubbleConfig.subtitleTextColor, onChange: (e) => updateConfig('subtitleTextColor', e.target.value), className: "h-10 w-16 rounded border p-1" }), jsx(Input, { value: customFloatingBubbleConfig.subtitleTextColor, onChange: (e) => updateConfig('subtitleTextColor', e.target.value), placeholder: "#e5e7eb", className: "flex-1" })] })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "borderRadius", className: "text-sm text-gray-600", children: t('tabsEmbedTab.borderRadius') }), jsx("div", { className: "mt-2 flex gap-2", children: jsx(Input, { value: customFloatingBubbleConfig.borderRadius, onChange: (e) => updateConfig('borderRadius', e.target.value), placeholder: "16", type: "range", min: 0, max: 100, step: 1, className: "flex-1", id: "borderRadius" }) })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "imageSize", className: "text-sm text-gray-600", children: t('tabsEmbedTab.imageSize') }), jsx("div", { className: "mt-2 flex gap-2", children: jsx(Input, { value: customFloatingBubbleConfig.imageSize, onChange: (e) => updateConfig('imageSize', e.target.value), placeholder: "16", type: "range", min: 0, max: 100, step: 1, className: "flex-1" }) })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "fontSize", className: "text-sm text-gray-600", children: t('tabsEmbedTab.titleFontSize') }), jsxs("div", { className: "mt-2 flex gap-2", children: [jsx(Input, { id: "fontSize", value: customFloatingBubbleConfig.fontSize, onChange: (e) => updateConfig('fontSize', e.target.value), placeholder: "16", type: "range", min: 10, max: 24, step: 1, className: "flex-1" }), jsxs("span", { className: "flex min-w-[30px] items-center text-xs text-gray-500", children: [customFloatingBubbleConfig.fontSize, "px"] })] })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "subtitleFontSize", className: "text-sm text-gray-600", children: t('tabsEmbedTab.subtitleFontSize') }), jsxs("div", { className: "mt-2 flex gap-2", children: [jsx(Input, { id: "subtitleFontSize", value: customFloatingBubbleConfig.subtitleFontSize, onChange: (e) => updateConfig('subtitleFontSize', e.target.value), placeholder: "12", type: "range", min: 8, max: 20, step: 1, className: "flex-1" }), jsxs("span", { className: "flex min-w-[30px] items-center text-xs text-gray-500", children: [customFloatingBubbleConfig.subtitleFontSize, "px"] })] })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "shadow", className: "text-sm text-gray-600", children: t('tabsEmbedTab.useShadow') }), jsx("div", { className: "mt-2", children: jsxs(Select$1, { value: customFloatingBubbleConfig.shadow ? '1' : '0', onValueChange: (value) => updateConfig('shadow', value === '1'), children: [jsx(SelectTrigger, { children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: '1', children: t('tabsEmbedTab.yes') }), jsx(SelectItem, { value: '0', children: t('tabsEmbedTab.no') })] })] }) })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "padding", className: "text-sm text-gray-600", children: t('tabsEmbedTab.padding') }), jsx("div", { className: "mt-2 flex gap-2", children: jsx(Input, { value: customFloatingBubbleConfig.padding, onChange: (e) => updateConfig('padding', e.target.value), placeholder: "1", type: "range", min: 0, max: 50, step: 1, className: "flex-1" }) })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "strokeWidth", className: "text-sm text-gray-600", children: t('tabsEmbedTab.strokeThickness') }), jsx("div", { className: "mt-2 flex gap-2", children: jsx(Input, { value: customFloatingBubbleConfig.strokeWidth, onChange: (e) => updateConfig('strokeWidth', e.target.value), placeholder: "1", type: "range", min: 0, max: 10, step: 1, className: "flex-1" }) })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "strokeColor", className: "text-sm text-gray-600", children: t('tabsEmbedTab.strokeColor') }), jsxs("div", { className: "mt-2 flex gap-2", children: [jsx(Input, { id: "strokeColor", type: "color", value: customFloatingBubbleConfig.strokeColor, onChange: (e) => updateConfig('strokeColor', e.target.value), className: "h-10 w-16 rounded border p-1" }), jsx(Input, { value: customFloatingBubbleConfig.strokeColor, onChange: (e) => updateConfig('strokeColor', e.target.value), placeholder: "#3b82f6", className: "flex-1" })] })] })] }) })] }) }), jsx(TabsContent, { value: "position", className: "space-y-6", children: jsxs(Card$1, { children: [jsx(CardHeader, { className: "mb-0 px-4 py-3", children: jsx(CardTitle, { className: "text-sm font-medium text-gray-600", children: t('tabsEmbedTab.positionLayout') }) }), jsx(CardContent, { className: "space-y-6 pt-0", children: jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "position", className: "text-sm text-gray-600", children: t('tabsEmbedTab.screenPosition') }), jsx("div", { className: "mt-2", children: jsxs(Select$1, { value: customFloatingBubbleConfig.position, onValueChange: (value) => updateConfig('position', value), children: [jsx(SelectTrigger, { children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "bottom-right", children: t('tabsEmbedTab.bottomRight') }), jsx(SelectItem, { value: "bottom-left", children: t('tabsEmbedTab.bottomLeft') }), jsx(SelectItem, { value: "top-right", children: t('tabsEmbedTab.topRight') }), jsx(SelectItem, { value: "top-left", children: t('tabsEmbedTab.topLeft') })] })] }) })] }) })] }) }), jsx(TabsContent, { value: "content", className: "space-y-6", children: jsxs(Card$1, { children: [jsx(CardHeader, { className: "mb-0 px-4 py-3", children: jsx(CardTitle, { className: "text-sm font-medium text-gray-600", children: t('tabsEmbedTab.textContent') }) }), jsxs(CardContent, { className: "space-y-4 pt-0", children: [jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "title", className: "text-sm text-gray-600", children: t('tabsEmbedTab.titleLabel') }), jsx(Input, { id: "title", value: customFloatingBubbleConfig.title, onChange: (e) => updateConfig('title', e.target.value), placeholder: "AI-powered assistant", className: "mt-2" })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "subtitle", className: "text-sm text-gray-600", children: t('tabsEmbedTab.subtitleText') }), jsx(Input, { id: "subtitle", value: customFloatingBubbleConfig.subtitle, onChange: (e) => updateConfig('subtitle', e.target.value), placeholder: "Created with AI", className: "mt-2" })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "iconImage", className: "text-sm text-gray-600", children: t('tabsEmbedTab.iconImage') }), jsxs("div", { className: "mt-2 space-y-1", children: [jsx(Input, { id: "iconImage", type: "file", accept: "image/*", onChange: (e) => {
280062
282200
  var _a;
280063
282201
  const file = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0];
280064
282202
  if (file) {
@@ -280073,7 +282211,7 @@ function AgentEmbedTab({ labels: labelsProp, urls, CopyCodeBlock, visibilityOpti
280073
282211
  }
280074
282212
  }, className: "h-[45px] file:mr-4 file:rounded-full file:border-0 file:bg-blue-50 file:px-4 file:py-2 file:text-sm file:font-semibold file:text-blue-700 hover:file:bg-blue-100" }), customFloatingBubbleConfig.image && (jsxs("div", { className: "my-3 flex items-center gap-3", children: [jsx("img", { src: customFloatingBubbleConfig.image || '/placeholder.svg', alt: t('tabsEmbedTab.chatIconPreviewAlt'), className: "h-12 w-12 rounded-lg border bg-gray-100 object-cover p-1" }), jsx(Button$1, { type: "button", variant: "outline", size: "sm", onClick: () => updateMultipleConfig({
280075
282213
  image: null,
280076
- }), children: t('tabsEmbedTab.removeImage') })] })), jsx("p", { className: "text-xs text-gray-500", children: t('tabsEmbedTab.iconUploadHint') })] })] })] })] }) })] }), jsxs(Card, { children: [jsx(CardHeader, { className: "mb-0 pt-4 pb-0", children: jsx(CardTitle, { className: "text-sm font-medium text-gray-600", children: t('tabsEmbedTab.livePreview') }) }), jsx(CardContent, { children: jsx(InteractiveBubbleConfigDisplay, { customFloatingBubbleConfig: customFloatingBubbleConfig, handleFloatingBubbleImageError: handleFloatingBubbleImageError }) })] })] }) }))] }) }), jsx("div", { className: "flex flex-shrink-0 justify-end border-t border-gray-200 bg-white px-3 py-4", children: jsx(Button$1, { onClick: () => form.handleSubmit(), className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-sm text-white hover:text-white hover:opacity-90", disabled: form.state.isSubmitting, children: form.state.isSubmitting ? labels.actions.creatingEmbed : labels.actions.createEmbed }) })] }), embedCode && (jsx(Dialog, { open: !!embedCode, onOpenChange: (open) => {
282214
+ }), children: t('tabsEmbedTab.removeImage') })] })), jsx("p", { className: "text-xs text-gray-500", children: t('tabsEmbedTab.iconUploadHint') })] })] })] })] }) })] }), jsxs(Card$1, { children: [jsx(CardHeader, { className: "mb-0 pt-4 pb-0", children: jsx(CardTitle, { className: "text-sm font-medium text-gray-600", children: t('tabsEmbedTab.livePreview') }) }), jsx(CardContent, { children: jsx(InteractiveBubbleConfigDisplay, { customFloatingBubbleConfig: customFloatingBubbleConfig, handleFloatingBubbleImageError: handleFloatingBubbleImageError }) })] })] }) }))] }) }), jsx("div", { className: "flex flex-shrink-0 justify-end border-t border-gray-200 bg-white px-3 py-4", children: jsx(Button$1, { onClick: () => form.handleSubmit(), className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-sm text-white hover:text-white hover:opacity-90", disabled: form.state.isSubmitting, children: form.state.isSubmitting ? labels.actions.creatingEmbed : labels.actions.createEmbed }) })] }), embedCode && (jsx(Dialog, { open: !!embedCode, onOpenChange: (open) => {
280077
282215
  if (!open) {
280078
282216
  setEmbedCode('');
280079
282217
  }
@@ -282549,7 +284687,7 @@ function AgentCard({ agent, onClick, variant = 'default', isStarred = false, isT
282549
284687
  onClick(agent);
282550
284688
  }
282551
284689
  };
282552
- return (jsx(Card, { className: "cursor-pointer rounded-lg border border-[#D0E0FF] bg-[#F5F8FF] transition-shadow hover:shadow-md md:h-full", onClick: handleCardClick, tabIndex: 0, onKeyDown: handleCardKeyDown, "aria-label": `${exploreAriaPrefix} ${agent.name}. ${agent.description || ''}`, children: jsx(CardContent, { className: "p-6 md:h-full", children: jsxs("div", { className: "flex items-start gap-4 md:h-full", children: [jsxs(Avatar, { className: "h-12 w-12 shrink-0 rounded-full", children: [jsx(AvatarImage, { src: agent.profile_image, alt: agent.name, className: "object-cover" }), jsx(AvatarFallback, { children: agent.name.substring(0, 2).toUpperCase() })] }), jsxs("div", { className: "min-w-0 flex-1", children: [jsxs("div", { className: "mb-2 flex items-center justify-between", children: [jsx("h3", { className: "text-sm font-medium text-gray-900", children: agent.name }), variant === 'with-star' && onToggleStar && (jsx(StarButton, { isStarred: isStarred, isToggling: isTogglingStar, disabled: disableStar, onClick: (e) => {
284690
+ return (jsx(Card$1, { className: "cursor-pointer rounded-lg border border-[#D0E0FF] bg-[#F5F8FF] transition-shadow hover:shadow-md md:h-full", onClick: handleCardClick, tabIndex: 0, onKeyDown: handleCardKeyDown, "aria-label": `${exploreAriaPrefix} ${agent.name}. ${agent.description || ''}`, children: jsx(CardContent, { className: "p-6 md:h-full", children: jsxs("div", { className: "flex items-start gap-4 md:h-full", children: [jsxs(Avatar, { className: "h-12 w-12 shrink-0 rounded-full", children: [jsx(AvatarImage, { src: agent.profile_image, alt: agent.name, className: "object-cover" }), jsx(AvatarFallback, { children: agent.name.substring(0, 2).toUpperCase() })] }), jsxs("div", { className: "min-w-0 flex-1", children: [jsxs("div", { className: "mb-2 flex items-center justify-between", children: [jsx("h3", { className: "text-sm font-medium text-gray-900", children: agent.name }), variant === 'with-star' && onToggleStar && (jsx(StarButton, { isStarred: isStarred, isToggling: isTogglingStar, disabled: disableStar, onClick: (e) => {
282553
284691
  e.stopPropagation();
282554
284692
  onToggleStar(agent, e);
282555
284693
  } }))] }), jsx("p", { className: "mb-3 line-clamp-2 text-sm leading-relaxed text-gray-600", children: agent.description }), agent.updated_at && (jsxs("p", { className: "text-xs text-gray-500", children: [updatedOnLabel, " ", formatDate(agent.updated_at)] }))] })] }) }) }));
@@ -282584,7 +284722,7 @@ function StarredAgentsSection() {
282584
284722
  onUnauthenticatedAction === null || onUnauthenticatedAction === void 0 ? void 0 : onUnauthenticatedAction();
282585
284723
  }, [username, onUnauthenticatedAction]);
282586
284724
  const sectionLabels = labels.sections.starred;
282587
- return (jsxs("div", { children: [jsx("h2", { className: "mb-4 text-lg font-medium text-gray-900", role: "heading", "aria-level": 2, children: sectionLabels.title }), agents.length > 0 ? (jsxs(Fragment$1, { children: [jsx("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2", "data-testid": "favorites-card-list", role: "list", "aria-label": sectionLabels.ariaLabel, children: agents.map((agent) => (jsx("div", { role: "listitem", children: jsx(AgentCard, { agent: agent, onClick: onAgentClick, variant: "with-star", isStarred: agent.starred, isTogglingStar: togglingAgentId === String(agent.id), onToggleStar: toggleFavorite, disableStar: !username, updatedOnLabel: labels.card.updatedOn, exploreAriaPrefix: labels.card.exploreAriaPrefix }) }, agent.id))) }), hasMore && (jsx("div", { className: "mt-6 flex justify-center", children: jsx(Button$1, { variant: "outline", onClick: () => setLimit((prev) => prev + FAVORITE_AGENTS_LIMIT), disabled: isFetching, "aria-label": sectionLabels.loadMoreAriaLabel, role: "button", children: isFetching ? (jsxs("div", { className: "flex items-center gap-2", children: [jsx(Spinner, { className: "h-4 w-4", "aria-hidden": "true" }), jsx("span", { children: labels.loadingMore })] })) : (labels.seeMore) }) }))] })) : (jsx("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3", children: jsx(Card, { "data-testid": "favorites-card", className: "cursor-pointer rounded-lg border border-[#D0E0FF] bg-[#F5F8FF] transition-shadow duration-200 hover:shadow-md md:col-span-2 lg:col-span-3", onClick: handleFavoriteCardClick, role: "button", tabIndex: 0, onKeyDown: (e) => {
284725
+ return (jsxs("div", { children: [jsx("h2", { className: "mb-4 text-lg font-medium text-gray-900", role: "heading", "aria-level": 2, children: sectionLabels.title }), agents.length > 0 ? (jsxs(Fragment$1, { children: [jsx("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2", "data-testid": "favorites-card-list", role: "list", "aria-label": sectionLabels.ariaLabel, children: agents.map((agent) => (jsx("div", { role: "listitem", children: jsx(AgentCard, { agent: agent, onClick: onAgentClick, variant: "with-star", isStarred: agent.starred, isTogglingStar: togglingAgentId === String(agent.id), onToggleStar: toggleFavorite, disableStar: !username, updatedOnLabel: labels.card.updatedOn, exploreAriaPrefix: labels.card.exploreAriaPrefix }) }, agent.id))) }), hasMore && (jsx("div", { className: "mt-6 flex justify-center", children: jsx(Button$1, { variant: "outline", onClick: () => setLimit((prev) => prev + FAVORITE_AGENTS_LIMIT), disabled: isFetching, "aria-label": sectionLabels.loadMoreAriaLabel, role: "button", children: isFetching ? (jsxs("div", { className: "flex items-center gap-2", children: [jsx(Spinner, { className: "h-4 w-4", "aria-hidden": "true" }), jsx("span", { children: labels.loadingMore })] })) : (labels.seeMore) }) }))] })) : (jsx("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3", children: jsx(Card$1, { "data-testid": "favorites-card", className: "cursor-pointer rounded-lg border border-[#D0E0FF] bg-[#F5F8FF] transition-shadow duration-200 hover:shadow-md md:col-span-2 lg:col-span-3", onClick: handleFavoriteCardClick, role: "button", tabIndex: 0, onKeyDown: (e) => {
282588
284726
  if (e.key === 'Enter' || e.key === ' ') {
282589
284727
  e.preventDefault();
282590
284728
  handleFavoriteCardClick();
@@ -282619,7 +284757,7 @@ function FeaturedAgentsSection() {
282619
284757
  if (agents.length === 0 && !isFetching) {
282620
284758
  return null;
282621
284759
  }
282622
- return (jsxs("div", { children: [jsx("h2", { className: "mb-4 text-lg font-medium text-gray-900", role: "heading", "aria-level": 2, children: sectionLabels.title }), agents.length > 0 ? (jsxs(Fragment$1, { children: [jsx("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2", children: agents.map((agent) => (jsx(AgentCard, { agent: agent, onClick: onAgentClick, variant: "with-star", isStarred: agent.starred, isTogglingStar: togglingAgentId === String(agent.id), onToggleStar: toggleFavorite, disableStar: !username, updatedOnLabel: labels.card.updatedOn, exploreAriaPrefix: labels.card.exploreAriaPrefix }, agent.id))) }), hasMore && (jsx("div", { className: "mt-6 flex justify-center", children: jsx(Button$1, { variant: "outline", onClick: () => setLimit((prev) => prev + FEATURED_AGENTS_LIMIT), disabled: isFetching, "aria-label": sectionLabels.loadMoreAriaLabel, children: isFetching ? (jsxs("div", { className: "flex items-center gap-2", children: [jsx(Spinner, { className: "h-4 w-4", "aria-hidden": "true" }), jsx("span", { children: labels.loadingMore })] })) : (labels.seeMore) }) }))] })) : (jsx("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3", children: jsx(Card, { className: "rounded-lg border border-[#D0E0FF] bg-[#F5F8FF] md:col-span-2 lg:col-span-3", children: jsx(CardContent, { className: "p-6", children: jsxs("div", { className: "flex items-start gap-4", children: [jsx("div", { className: "flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-lg bg-[#D0E0FF]", children: jsx(Award, { className: "h-6 w-6 text-[#38A1E5]" }) }), jsxs("div", { className: "min-w-0 flex-1", children: [jsx("h3", { className: "mb-2 text-sm font-medium text-gray-900", children: sectionLabels.emptyTitle }), jsx("p", { className: "mb-3 text-sm leading-relaxed text-gray-600", children: sectionLabels.emptyDescription }), jsx("p", { className: "text-xs text-gray-500", children: sectionLabels.emptySubtext })] })] }) }) }) }))] }));
284760
+ return (jsxs("div", { children: [jsx("h2", { className: "mb-4 text-lg font-medium text-gray-900", role: "heading", "aria-level": 2, children: sectionLabels.title }), agents.length > 0 ? (jsxs(Fragment$1, { children: [jsx("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2", children: agents.map((agent) => (jsx(AgentCard, { agent: agent, onClick: onAgentClick, variant: "with-star", isStarred: agent.starred, isTogglingStar: togglingAgentId === String(agent.id), onToggleStar: toggleFavorite, disableStar: !username, updatedOnLabel: labels.card.updatedOn, exploreAriaPrefix: labels.card.exploreAriaPrefix }, agent.id))) }), hasMore && (jsx("div", { className: "mt-6 flex justify-center", children: jsx(Button$1, { variant: "outline", onClick: () => setLimit((prev) => prev + FEATURED_AGENTS_LIMIT), disabled: isFetching, "aria-label": sectionLabels.loadMoreAriaLabel, children: isFetching ? (jsxs("div", { className: "flex items-center gap-2", children: [jsx(Spinner, { className: "h-4 w-4", "aria-hidden": "true" }), jsx("span", { children: labels.loadingMore })] })) : (labels.seeMore) }) }))] })) : (jsx("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3", children: jsx(Card$1, { className: "rounded-lg border border-[#D0E0FF] bg-[#F5F8FF] md:col-span-2 lg:col-span-3", children: jsx(CardContent, { className: "p-6", children: jsxs("div", { className: "flex items-start gap-4", children: [jsx("div", { className: "flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-lg bg-[#D0E0FF]", children: jsx(Award, { className: "h-6 w-6 text-[#38A1E5]" }) }), jsxs("div", { className: "min-w-0 flex-1", children: [jsx("h3", { className: "mb-2 text-sm font-medium text-gray-900", children: sectionLabels.emptyTitle }), jsx("p", { className: "mb-3 text-sm leading-relaxed text-gray-600", children: sectionLabels.emptyDescription }), jsx("p", { className: "text-xs text-gray-500", children: sectionLabels.emptySubtext })] })] }) }) }) }))] }));
282623
284761
  }
282624
284762
 
282625
284763
  function CustomAgentsSection() {
@@ -282651,7 +284789,7 @@ function CustomAgentsSection() {
282651
284789
  onCreateAgent === null || onCreateAgent === void 0 ? void 0 : onCreateAgent();
282652
284790
  }, [username, onUnauthenticatedAction, onCreateAgent]);
282653
284791
  const sectionLabels = labels.sections.custom;
282654
- const createCard = onCreateAgent ? (jsx(WithPermissions, { rbacResource: createAgentRbacResource, rbacPermissions: rbacPermissions !== null && rbacPermissions !== void 0 ? rbacPermissions : {}, children: ({ hasPermission }) => hasPermission || !enableRBAC ? (jsx(Card, { className: "cursor-pointer border border-[#D0E0FF] bg-[#F5F8FF] transition-shadow duration-200 hover:shadow-md", onClick: handleCreate, role: "button", tabIndex: 0, onKeyDown: (e) => {
284792
+ const createCard = onCreateAgent ? (jsx(WithPermissions, { rbacResource: createAgentRbacResource, rbacPermissions: rbacPermissions !== null && rbacPermissions !== void 0 ? rbacPermissions : {}, children: ({ hasPermission }) => hasPermission || !enableRBAC ? (jsx(Card$1, { className: "cursor-pointer border border-[#D0E0FF] bg-[#F5F8FF] transition-shadow duration-200 hover:shadow-md", onClick: handleCreate, role: "button", tabIndex: 0, onKeyDown: (e) => {
282655
284793
  if (e.key === 'Enter' || e.key === ' ') {
282656
284794
  e.preventDefault();
282657
284795
  handleCreate();
@@ -283839,76 +285977,6 @@ const AIMessageBubble = forwardRef(function AIMessageBubble({ content, profileIm
283839
285977
  }, className: "-ml-1 text-gray-500 hover:text-gray-700", children: [jsx("span", { className: "sr-only", children: t('chatAiMessageBubble.retryScreenReaderLabel') }), jsx(RefreshCcw, { className: "h-4 w-4" })] }) }), jsx(TooltipContent, { className: "ibl-tooltip-content", children: t('chatAiMessageBubble.retryTooltip') })] }))] })] })] }) }) }));
283840
285978
  });
283841
285979
 
283842
- function FileCard({ fileName, fileType }) {
283843
- const getFileIcon = (fileName, fileType) => {
283844
- const type = fileType.toLowerCase();
283845
- const name = fileName.toLowerCase();
283846
- const extension = name.split('.').pop() || '';
283847
- // Check by MIME type first
283848
- if (type.includes('pdf')) {
283849
- return jsx(File$1, { className: "h-5 w-5 text-blue-500" });
283850
- }
283851
- else if (type.includes('image')) {
283852
- return jsx(FileImage, { className: "h-5 w-5 text-blue-500" });
283853
- }
283854
- else if (type.includes('audio')) {
283855
- return jsx(FileAudio, { className: "h-5 w-5 text-blue-500" });
283856
- }
283857
- else if (type.includes('video')) {
283858
- return jsx(FilePlay, { className: "h-5 w-5 text-blue-500" });
283859
- }
283860
- else if (type.includes('spreadsheet') || type.includes('excel') || type.includes('csv')) {
283861
- return jsx(FileSpreadsheet, { className: "h-5 w-5 text-blue-500" });
283862
- }
283863
- else if (type.includes('zip') || type.includes('compressed') || type.includes('archive')) {
283864
- return jsx(FileArchive, { className: "h-5 w-5 text-blue-500" });
283865
- }
283866
- else if (type.includes('javascript') ||
283867
- type.includes('typescript') ||
283868
- type.includes('html') ||
283869
- type.includes('css') ||
283870
- type.includes('json')) {
283871
- return jsx(FileCode, { className: "h-5 w-5 text-blue-500" });
283872
- }
283873
- // If MIME type doesn't match, check by extension
283874
- if (['doc', 'docx', 'txt', 'rtf'].includes(extension)) {
283875
- return jsx(FileText, { className: "h-5 w-5 text-blue-500" });
283876
- }
283877
- else if (['jpg', 'jpeg', 'png', 'gif', 'svg', 'webp'].includes(extension)) {
283878
- return jsx(FileImage, { className: "h-5 w-5 text-blue-500" });
283879
- }
283880
- else if (['pdf'].includes(extension)) {
283881
- return jsx(File$1, { className: "h-5 w-5 text-blue-500" });
283882
- }
283883
- else if (['mp3', 'wav', 'ogg', 'flac'].includes(extension)) {
283884
- return jsx(FileAudio, { className: "h-5 w-5 text-blue-500" });
283885
- }
283886
- else if (['mp4', 'webm', 'avi', 'mov'].includes(extension)) {
283887
- return jsx(FilePlay, { className: "h-5 w-5 text-blue-500" });
283888
- }
283889
- else if (['xls', 'xlsx', 'csv'].includes(extension)) {
283890
- return jsx(FileSpreadsheet, { className: "h-5 w-5 text-blue-500" });
283891
- }
283892
- else if (['zip', 'rar', '7z', 'tar', 'gz'].includes(extension)) {
283893
- return jsx(FileArchive, { className: "h-5 w-5 text-blue-500" });
283894
- }
283895
- else if (['js', 'ts', 'jsx', 'tsx', 'html', 'css', 'json', 'py', 'java', 'c', 'cpp'].includes(extension)) {
283896
- return jsx(FileCode, { className: "h-5 w-5 text-blue-500" });
283897
- }
283898
- // Default icon
283899
- return jsx(File$1, { className: "h-5 w-5 text-blue-500" });
283900
- };
283901
- return (jsxs("div", { className: "mb-2 flex max-w-xs items-center rounded-lg border border-gray-200 bg-white p-3", children: [jsx("div", { className: "mr-3 shrink-0 rounded-lg bg-blue-100 p-2 text-blue-600", children: getFileIcon(fileName, fileType) }), jsxs("div", { className: "min-w-0 flex-1", children: [jsx("h3", { className: "truncate text-sm font-medium text-gray-900", children: fileName }), jsx("p", { className: "truncate text-xs text-gray-500", children: fileType })] })] }));
283902
- }
283903
-
283904
- function ImageMessage({ url, fileName, setPreviewImage }) {
283905
- const [hasError, setHasError] = useState(false);
283906
- if (hasError || !url) {
283907
- return jsx(FileCard, { fileName: fileName, fileType: "image/*" });
283908
- }
283909
- return (jsx("div", { className: "h-full max-h-64 w-full max-w-96 overflow-hidden rounded-lg", children: jsx("div", { className: "relative", children: jsx("img", { src: url, alt: fileName, className: "h-auto w-full cursor-pointer object-contain p-2", onClick: () => setPreviewImage(url), onError: () => setHasError(true) }) }) }));
283910
- }
283911
-
283912
285980
  function UserMessageBubble({ message, isHighlighted, profileImage, mentorName, messages, onHighlightMessage, onPreviewImage, }) {
283913
285981
  return (jsxs("div", { className: `message-container mb-4 flex flex-col items-end transition-all duration-300 ${isHighlighted ? 'rounded-2xl bg-blue-100' : ''}`, children: [message.fileAttachments && message.fileAttachments.length > 0 && (jsx("div", { className: "mb-2 flex flex-col items-end gap-2", children: message.fileAttachments.map((attachment, idx) => {
283914
285982
  if (attachment.fileType.startsWith('image/')) {
@@ -285942,7 +288010,7 @@ function ExploreMentors({ tenantKey, onBrowseAll, onSelectMentor }) {
285942
288010
  name: mentor.name,
285943
288011
  // @ts-expect-error - description property may not exist on Mentor type
285944
288012
  description: mentor.description || t('welcomeChatExploreMentors.noDescription'),
285945
- }), children: jsx(Card, { className: "h-full cursor-pointer border border-[#D0E0FF] bg-[#F5F8FF] shadow-xs transition-all duration-200 focus-within:shadow-md hover:shadow-sm", children: jsx(CardContent, { className: "h-full p-4", children: jsxs("div", { className: "flex h-full items-start gap-3", children: [jsxs(Avatar, { className: "h-12 w-12 flex-shrink-0", children: [jsx(AvatarImage
288013
+ }), children: jsx(Card$1, { className: "h-full cursor-pointer border border-[#D0E0FF] bg-[#F5F8FF] shadow-xs transition-all duration-200 focus-within:shadow-md hover:shadow-sm", children: jsx(CardContent, { className: "h-full p-4", children: jsxs("div", { className: "flex h-full items-start gap-3", children: [jsxs(Avatar, { className: "h-12 w-12 flex-shrink-0", children: [jsx(AvatarImage
285946
288014
  // @ts-expect-error - profile_image property may not exist on Mentor type
285947
288015
  , {
285948
288016
  // @ts-expect-error - profile_image property may not exist on Mentor type
@@ -285990,7 +288058,7 @@ function ConversationStarters({ onTemplateSelect, enabledGuidedPrompts, sessionI
285990
288058
  }
285991
288059
  return (jsxs("div", { className: "mx-auto w-full max-w-6xl px-4", children: [jsx("h2", { className: "mb-6 text-xl font-semibold text-gray-900", children: t('welcomeChatConversationStarters.heading') }), jsx("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5", children: (_b = guidedPrompts === null || guidedPrompts === void 0 ? void 0 : guidedPrompts.results) === null || _b === void 0 ? void 0 : _b.map((guidedPrompt, index) => {
285992
288060
  var _a;
285993
- return (jsx(Card, { className: cn('h-full border border-[#D0E0FF] bg-[#F5F8FF] shadow-xs transition-all duration-200', isChatDisabledByRbac
288061
+ return (jsx(Card$1, { className: cn('h-full border border-[#D0E0FF] bg-[#F5F8FF] shadow-xs transition-all duration-200', isChatDisabledByRbac
285994
288062
  ? 'cursor-not-allowed opacity-50'
285995
288063
  : 'cursor-pointer hover:shadow-sm focus:shadow-md focus:ring-2 focus:ring-gray-300 focus:ring-offset-2 focus:outline-none'), onClick: () => !isChatDisabledByRbac && onTemplateSelect(guidedPrompt.prompt), onKeyDown: (e) => {
285996
288064
  if (isChatDisabledByRbac)
@@ -287447,7 +289515,7 @@ function TaskMetricsCards({ tasksData, labels }) {
287447
289515
  const totalTasks = tasksData.length;
287448
289516
  const completedTasks = tasksData.filter((t) => t.status === 'Completed').length;
287449
289517
  const failedTasks = tasksData.filter((t) => t.status === 'Failed').length;
287450
- return (jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-3 gap-4", children: [jsx(Card, { className: "w-full bg-white border border-blue-200", children: jsxs(CardContent, { className: "p-4 flex flex-row items-center justify-between", children: [jsxs("div", { className: "flex flex-col", children: [jsx("h3", { className: "text-sm font-medium text-gray-700 mb-2", children: labels.metrics.total }), jsx("p", { className: "text-2xl font-semibold text-gray-900", children: totalTasks })] }), jsx(ClipboardList, { className: "h-6 w-6 text-blue-500 flex-shrink-0" })] }) }), jsx(Card, { className: "w-full bg-white border border-blue-200", children: jsxs(CardContent, { className: "p-4 flex flex-row items-center justify-between", children: [jsxs("div", { className: "flex flex-col", children: [jsx("h3", { className: "text-sm font-medium text-gray-700 mb-2", children: labels.metrics.completed }), jsx("p", { className: "text-2xl font-semibold text-gray-900", children: completedTasks })] }), jsx(CircleCheckBig, { className: "h-6 w-6 text-blue-500 flex-shrink-0" })] }) }), jsx(Card, { className: "w-full bg-white border border-blue-200", children: jsxs(CardContent, { className: "p-4 flex flex-row items-center justify-between", children: [jsxs("div", { className: "flex flex-col", children: [jsx("h3", { className: "text-sm font-medium text-gray-700 mb-2", children: labels.metrics.failed }), jsx("p", { className: "text-2xl font-semibold text-gray-900", children: failedTasks })] }), jsx(CircleX, { className: "h-6 w-6 text-red-500 flex-shrink-0" })] }) })] }));
289518
+ return (jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-3 gap-4", children: [jsx(Card$1, { className: "w-full bg-white border border-blue-200", children: jsxs(CardContent, { className: "p-4 flex flex-row items-center justify-between", children: [jsxs("div", { className: "flex flex-col", children: [jsx("h3", { className: "text-sm font-medium text-gray-700 mb-2", children: labels.metrics.total }), jsx("p", { className: "text-2xl font-semibold text-gray-900", children: totalTasks })] }), jsx(ClipboardList, { className: "h-6 w-6 text-blue-500 flex-shrink-0" })] }) }), jsx(Card$1, { className: "w-full bg-white border border-blue-200", children: jsxs(CardContent, { className: "p-4 flex flex-row items-center justify-between", children: [jsxs("div", { className: "flex flex-col", children: [jsx("h3", { className: "text-sm font-medium text-gray-700 mb-2", children: labels.metrics.completed }), jsx("p", { className: "text-2xl font-semibold text-gray-900", children: completedTasks })] }), jsx(CircleCheckBig, { className: "h-6 w-6 text-blue-500 flex-shrink-0" })] }) }), jsx(Card$1, { className: "w-full bg-white border border-blue-200", children: jsxs(CardContent, { className: "p-4 flex flex-row items-center justify-between", children: [jsxs("div", { className: "flex flex-col", children: [jsx("h3", { className: "text-sm font-medium text-gray-700 mb-2", children: labels.metrics.failed }), jsx("p", { className: "text-2xl font-semibold text-gray-900", children: failedTasks })] }), jsx(CircleX, { className: "h-6 w-6 text-red-500 flex-shrink-0" })] }) })] }));
287451
289519
  }
287452
289520
 
287453
289521
  const STATUS_BADGE = {
@@ -288529,7 +290597,7 @@ function PromptCard({ prompt, onEdit, onSelect, title, tenantKey, userIsStudent
288529
290597
  onSelect(prompt.prompt);
288530
290598
  }
288531
290599
  };
288532
- return (jsxs(Fragment$1, { children: [jsx(Card, { className: "overflow-hidden border-0 bg-[#F8F8F8] transition-shadow hover:shadow-md", children: jsxs(CardContent, { className: "h-full p-4", children: [jsxs("div", { className: "mb-5 flex flex-1 items-start gap-2", children: [jsx(MessageSquare, { className: "mt-1 h-4 w-4 flex-shrink-0 text-gray-400" }), jsxs("div", { className: "flex-1", children: [jsx("h3", { className: "text-sm font-medium text-gray-600", children: title }), jsx(Markdown, { className: "mt-1 line-clamp-4 text-sm text-gray-500", children: parsePrompt(prompt.prompt) })] })] }), jsxs("div", { className: "flex flex-wrap justify-start gap-2", children: [!userIsStudent && (jsxs(Button$1, { variant: "outline", size: "sm", className: "flex h-7 items-center border-gray-200 px-3 text-xs font-normal text-gray-600", onClick: handleEdit, disabled: isDeleting, children: [jsx(SquarePen, { className: "mr-1.5 h-3 w-3" }), t('promptGalleryModalPromptCard.edit')] })), jsxs(Button$1, { variant: "outline", size: "sm", className: "flex h-7 items-center border-gray-200 px-3 text-xs font-normal text-gray-600", onClick: () => executeWithTrialCheck(handleRun), disabled: isDeleting, children: [jsx(Play, { className: "mr-1.5 h-3 w-3" }), t('promptGalleryModalPromptCard.run')] }), jsx(Button$1, { variant: "outline", size: "sm", className: "flex h-7 items-center border-gray-200 px-3 text-xs font-normal text-gray-600", onClick: () => copy(prompt.prompt), disabled: isDeleting, children: status === 'success' ? (jsxs(Fragment$1, { children: [jsx(Check, { className: "mr-1.5 h-3 w-3" }), " ", t('promptGalleryModalPromptCard.copied')] })) : (jsxs(Fragment$1, { children: [jsx(Copy, { className: "mr-1.5 h-3 w-3" }), " ", t('promptGalleryModalPromptCard.copy')] })) }), !userIsStudent && (jsxs(Button$1, { variant: "outline", size: "sm", className: "flex h-7 items-center border-gray-200 px-3 text-xs font-normal text-gray-600 hover:text-gray-600", onClick: () => executeWithTrialCheck(handleDelete), disabled: isDeleting, children: [jsx(DeleteIcon, { className: cn('mr-1.5 h-3 w-3', isDeleting && 'animate-spin') }), t('promptGalleryModalPromptCard.delete')] }))] })] }) }), isModalOpen && FreeTrialDialog && (jsx(FreeTrialDialog, { onClose: closeModal, isOpen: isModalOpen }))] }));
290600
+ return (jsxs(Fragment$1, { children: [jsx(Card$1, { className: "overflow-hidden border-0 bg-[#F8F8F8] transition-shadow hover:shadow-md", children: jsxs(CardContent, { className: "h-full p-4", children: [jsxs("div", { className: "mb-5 flex flex-1 items-start gap-2", children: [jsx(MessageSquare, { className: "mt-1 h-4 w-4 flex-shrink-0 text-gray-400" }), jsxs("div", { className: "flex-1", children: [jsx("h3", { className: "text-sm font-medium text-gray-600", children: title }), jsx(Markdown, { className: "mt-1 line-clamp-4 text-sm text-gray-500", children: parsePrompt(prompt.prompt) })] })] }), jsxs("div", { className: "flex flex-wrap justify-start gap-2", children: [!userIsStudent && (jsxs(Button$1, { variant: "outline", size: "sm", className: "flex h-7 items-center border-gray-200 px-3 text-xs font-normal text-gray-600", onClick: handleEdit, disabled: isDeleting, children: [jsx(SquarePen, { className: "mr-1.5 h-3 w-3" }), t('promptGalleryModalPromptCard.edit')] })), jsxs(Button$1, { variant: "outline", size: "sm", className: "flex h-7 items-center border-gray-200 px-3 text-xs font-normal text-gray-600", onClick: () => executeWithTrialCheck(handleRun), disabled: isDeleting, children: [jsx(Play, { className: "mr-1.5 h-3 w-3" }), t('promptGalleryModalPromptCard.run')] }), jsx(Button$1, { variant: "outline", size: "sm", className: "flex h-7 items-center border-gray-200 px-3 text-xs font-normal text-gray-600", onClick: () => copy(prompt.prompt), disabled: isDeleting, children: status === 'success' ? (jsxs(Fragment$1, { children: [jsx(Check, { className: "mr-1.5 h-3 w-3" }), " ", t('promptGalleryModalPromptCard.copied')] })) : (jsxs(Fragment$1, { children: [jsx(Copy, { className: "mr-1.5 h-3 w-3" }), " ", t('promptGalleryModalPromptCard.copy')] })) }), !userIsStudent && (jsxs(Button$1, { variant: "outline", size: "sm", className: "flex h-7 items-center border-gray-200 px-3 text-xs font-normal text-gray-600 hover:text-gray-600", onClick: () => executeWithTrialCheck(handleDelete), disabled: isDeleting, children: [jsx(DeleteIcon, { className: cn('mr-1.5 h-3 w-3', isDeleting && 'animate-spin') }), t('promptGalleryModalPromptCard.delete')] }))] })] }) }), isModalOpen && FreeTrialDialog && (jsx(FreeTrialDialog, { onClose: closeModal, isOpen: isModalOpen }))] }));
288533
290601
  }
288534
290602
 
288535
290603
  function EmptyPrompts() {