@iblai/iblai-js 2.3.5 → 2.3.7

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.
@@ -2,8 +2,8 @@ import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
2
2
  import * as React from 'react';
3
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';
4
4
  import * as dataLayer from '@iblai/data-layer';
5
- import { useGetUserMetadataQuery, useGetUserMetadataEdxQuery, 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, useGetMemsearchStatusQuery, useGetUserMemorySettingsQuery, useUpdateUserMemorySettingsMutation, useGetGlobalMemoriesQuery, useDeleteGlobalMemoryMutation, useInviteUserMutation, usePlatformInvitationsQuery, useCreateCatalogInvitationCourseBulkMutation, useGetCatalogInvitationsCourseQuery, useLazyPlatformUsersQuery, useLazyPlatformUserGroupsQuery, useGetPersonnalizedSearchQuery, useCreateCatalogInvitationProgramBulkMutation, useGetCatalogInvitationsProgramQuery, useUpdateUserRoleMutation, useUpdateUserStatusMutation, useUpdatePlatformUserRoleWithPoliciesMutation, usePlatformUsersQuery, 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, useDeleteApiKeyMutation, useGetApiKeyQuery, useGetApiKeyFieldPermissionsQuery, useUpdateApiKeyMutation, useGetApiKeysQuery, unwrapApiTokenList, useCreateApiKeyMutation, useCreateLLMCredentialMutation, useGetCredentialsSchemaQuery, useGetMaskedLLMCredentialsQuery, useGetLlmsQuery, useDeleteIntegrationCredentialMutation, useDeleteCredentialMutation, useCreateIntegrationCredentialMutation, useGetIntegrationCredentialsSchemaQuery, useGetMaskedIntegrationCredentialsQuery, useGetAccountBillingInfoQuery, useUpdateAutoRechargeInfoMutation, useTriggerAutoRechargeMutation, useCreateStripeCustomerPortalMutation, useSetPlatformConfigurationsMutation, useGetPlatformConfigurationsQuery, useUpdatePlatformMembershipMutation, useGetPlatformMembershipQuery, useGetCustomDomainsQuery, useCreateCustomDomainMutation, useDeleteCustomDomainMutation, useGetStudentMentorCreationStatusQuery, useSetStudentMentorCreationStatusMutation, coreApiSlice, recommendationPromptTypeEnum, useGetRecommendedPromptsListQuery, useCreateRecommendedPromptMutation, useUpdateRecommendedPromptMutation, useDeleteRecommendedPromptMutation, useLazyGetPublicPlatformImageAssetFileUrlQuery, useUpdateTenantMetadataMutation, useCreatePlatformImageAssetMutation, useGetProviderConfigQuery, useCreateProviderConfigMutation, useDeleteProviderConfigMutation, useGetExternalMappingQuery, useGetCredentialsListQuery, useCreateExternalMappingMutation, useDeleteExternalMappingMutation, useGetMemsearchConfigQuery, useUpdateMemsearchConfigMutation, useGetStripeConnectStatusQuery, useStartStripeConnectOnboardingMutation, useLazyGetStripeConnectDashboardQuery, useGetAiSearchMentorsQuery, useListPaywallsQuery, useListPricesQuery, useCreatePriceMutation, useUpdatePriceMutation, useDeletePriceMutation, useGetPaywallConfigQuery, useEnablePaywallMutation, useUpdatePaywallMutation, useLazyGetCourseMetaDataQuery, useLazyGetCourseCompletionOutlinesQuery, useLazyGetCourseEligibilityQuery, useLazyGetEdxSSOTokenQuery, useCreateCourseEnrollmentMutation, useCreateStripeCheckoutSessionMutation, useLazyGetCourseProgressQuery, useLazyGetCourseCompletionQuery, useUpdateExamAttemptMutation, useStartExamMutation, useLazyGetExamInfoQuery, useGetAgentSkillResourcesQuery, useCreateAgentSkillResourceMutation, useUpdateAgentSkillResourceMutation, useUploadAgentSkillResourceAssetMutation, useDeleteAgentSkillResourceMutation, useGetAgentSkillsQuery, useGetMentorSkillAssignmentsQuery, useCreateMentorSkillAssignmentMutation, useUpdateMentorSkillAssignmentMutation, useDeleteMentorSkillAssignmentMutation, useCreateAgentSkillMutation, useUpdateAgentSkillMutation, useDeleteAgentSkillMutation, resolveEffectiveAgentSkills, filterSlashSkills, useUpdateUserProjectMutation, useGetTrainingDocumentsQuery, useGetVectorDocumentsQuery, useGetMentorMemoriesListQuery, useGetMemoryCategoriesAdminQuery, useDeleteMentorMemoryMutation, useUpdateMentorMemoryMutation, useCreateMentorMemoryMutation, useLazyGetConnectedServiceAuthUrlQuery, useStarMentorMutation, useUnstarMentorMutation, 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, useGetMentorGraderConfigurationQuery, useListGraderCriteriaQuery, useCreateMentorGraderConfigurationMutation, useUpdateMentorGraderConfigurationMutation, useCreateGraderCriterionMutation, useUpdateGraderCriterionMutation, useDeleteGraderCriterionMutation, useGetMentorPublicSettingsQuery, useGetChatHistoryFilterQuery, useGetChatHistoryQuery, useGetMentorSummariesQuery, useGetConversationMemoriesQuery, useGetSupportTicketsQuery, useGetSupportTicketMessagesQuery, useCreateSupportTicketMessageMutation, usePatchSupportTicketMutation, useCloseSupportTicketMutation, useCreateLtiMentorMutation, useUpdateLtiMentorMutation, useGetLtiMentorsQuery, 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, useDeletePromptMutation, useDeleteTrainingDocumentMutation, useGetTrainingDocumentRetrainScheduleQuery, useCreateTrainingDocumentRetrainScheduleMutation, useCreateMemoryCategoryMutation, useUpdateMemoryCategoryMutation, useDeleteMemoryCategoryMutation, useUpdateArtifactMutation, useLazyGetArtifactVersionQuery, useLazyListArtifactVersionsQuery, useSetCurrentVersionMutation, useListArtifactVersionsQuery, useLazyGetArtifactQuery, useLazyListArtifactsQuery, useEditSessionMutation } from '@iblai/data-layer';
6
- import { getInitials, useTenantMetadata, WithPermissions, canWriteField, useStripeUpgrade, openExternalUrl, CHAT_AREA_SIZE, isAlphaNumeric32, checkRbacPermission, selectNumberOfActiveChatMessages, useUsername, selectStreaming, isLoggedIn, TOOLS, hasRemoteAiConfig, isSafariBrowser, useShowAttachment, useShowVoiceCall, useShowVoiceRecorder, useMentorSettings, selectShowingSharedChat, selectRbacPermissions, 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, 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, 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, useGetMemsearchStatusQuery, useGetUserMemorySettingsQuery, useUpdateUserMemorySettingsMutation, useGetGlobalMemoriesQuery, useGetAllMentorMemoriesQuery, useDeleteGlobalMemoryMutation, 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, useDeleteApiKeyMutation, useGetApiKeyQuery, useGetApiKeyFieldPermissionsQuery, useUpdateApiKeyMutation, useGetApiKeysQuery, unwrapApiTokenList, useCreateApiKeyMutation, useCreateLLMCredentialMutation, useGetCredentialsSchemaQuery, useGetMaskedLLMCredentialsQuery, useGetLlmsQuery, useDeleteIntegrationCredentialMutation, useDeleteCredentialMutation, useCreateIntegrationCredentialMutation, useGetIntegrationCredentialsSchemaQuery, useGetMaskedIntegrationCredentialsQuery, useGetAccountBillingInfoQuery, useUpdateAutoRechargeInfoMutation, useTriggerAutoRechargeMutation, useCreateStripeCustomerPortalMutation, useSetPlatformConfigurationsMutation, useGetPlatformConfigurationsQuery, useUpdatePlatformMembershipMutation, useGetPlatformMembershipQuery, useGetCustomDomainsQuery, useCreateCustomDomainMutation, useDeleteCustomDomainMutation, useGetStudentMentorCreationStatusQuery, useSetStudentMentorCreationStatusMutation, coreApiSlice, recommendationPromptTypeEnum, useGetRecommendedPromptsListQuery, useCreateRecommendedPromptMutation, useUpdateRecommendedPromptMutation, useDeleteRecommendedPromptMutation, useLazyGetPublicPlatformImageAssetFileUrlQuery, useUpdateTenantMetadataMutation, useCreatePlatformImageAssetMutation, useGetProviderConfigQuery, useCreateProviderConfigMutation, useDeleteProviderConfigMutation, useGetExternalMappingQuery, useGetCredentialsListQuery, useCreateExternalMappingMutation, useDeleteExternalMappingMutation, useGetMemsearchConfigQuery, useUpdateMemsearchConfigMutation, useGetStripeConnectStatusQuery, useStartStripeConnectOnboardingMutation, useLazyGetStripeConnectDashboardQuery, useGetAiSearchMentorsQuery, useListPaywallsQuery, useListPricesQuery, useCreatePriceMutation, useUpdatePriceMutation, useDeletePriceMutation, useGetPaywallConfigQuery, useEnablePaywallMutation, useUpdatePaywallMutation, useStarMentorMutation, useUnstarMentorMutation, useLazyGetCourseMetaDataQuery, useLazyGetCourseCompletionOutlinesQuery, useLazyGetCourseEligibilityQuery, useLazyGetEdxSSOTokenQuery, useCreateCourseEnrollmentMutation, useCreateStripeCheckoutSessionMutation, useLazyGetCourseProgressQuery, useLazyGetCourseCompletionQuery, useUpdateExamAttemptMutation, useStartExamMutation, useLazyGetExamInfoQuery, useGetAgentSkillResourcesQuery, useCreateAgentSkillResourceMutation, useUpdateAgentSkillResourceMutation, useUploadAgentSkillResourceAssetMutation, useDeleteAgentSkillResourceMutation, useGetAgentSkillsQuery, useGetMentorSkillAssignmentsQuery, useCreateMentorSkillAssignmentMutation, useUpdateMentorSkillAssignmentMutation, useDeleteMentorSkillAssignmentMutation, useCreateAgentSkillMutation, useUpdateAgentSkillMutation, useDeleteAgentSkillMutation, resolveEffectiveAgentSkills, filterSlashSkills, useUpdateUserProjectMutation, useGetTrainingDocumentsQuery, useGetVectorDocumentsQuery, useGetMentorMemoriesListQuery, useGetMemoryCategoriesAdminQuery, useDeleteMentorMemoryMutation, useUpdateMentorMemoryMutation, useCreateMentorMemoryMutation, 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, useGetMentorPublicSettingsQuery, useGetChatHistoryQuery, useGetMentorSummariesQuery, useGetConversationMemoriesQuery, useGetSupportTicketsQuery, useGetSupportTicketMessagesQuery, useCreateSupportTicketMessageMutation, usePatchSupportTicketMutation, useCloseSupportTicketMutation, useCreateLtiMentorMutation, useUpdateLtiMentorMutation, useGetLtiMentorsQuery, 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, useDeletePromptMutation, useDeleteTrainingDocumentMutation, useGetTrainingDocumentRetrainScheduleQuery, useCreateTrainingDocumentRetrainScheduleMutation, useCreateMemoryCategoryMutation, useUpdateMemoryCategoryMutation, useDeleteMemoryCategoryMutation, useUpdateArtifactMutation, useLazyGetArtifactVersionQuery, useLazyListArtifactVersionsQuery, useSetCurrentVersionMutation, useListArtifactVersionsQuery, useLazyGetArtifactQuery, useLazyListArtifactsQuery, useEditSessionMutation } from '@iblai/data-layer';
6
+ import { getInitials, useTenantMetadata, useCurrentTenant, selectRbacPermissions, 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, 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';
@@ -97537,6 +97537,37 @@ var analyticsAnalyticsFinancialStats$3 = {
97537
97537
  goToLastPage: "Go to last page",
97538
97538
  paginationShowing: "Showing {from} to {to} of {total} results"
97539
97539
  };
97540
+ var analyticsAnalyticsMemoryStats$3 = {
97541
+ noPermission: "You do not have permission to view user memories.",
97542
+ scopeLabel: "Memory scope",
97543
+ scopeMentor: "This Agent",
97544
+ scopeAllMentors: "All Agents",
97545
+ scopeGlobal: "Global",
97546
+ searchAgents: "Search Agents…",
97547
+ searchAgentsTitle: "Search Agents",
97548
+ clearAgentSearch: "Clear Agent Search",
97549
+ contentPlaceholder: "Search memory content",
97550
+ pickDateRange: "Pick a Date Range",
97551
+ allCategories: "All Categories",
97552
+ byCategory: "Memories by Category",
97553
+ categoryCount: "{count} ({percent}%)",
97554
+ loadError: "An error occurred while loading memories.",
97555
+ noMemories: "No memories have been captured yet.",
97556
+ detailTitle: "Memory Detail",
97557
+ detailUser: "User",
97558
+ detailAgent: "Agent",
97559
+ detailCategory: "Category",
97560
+ detailSource: "Source",
97561
+ detailCaptured: "Captured",
97562
+ detailSession: "Session",
97563
+ sourceAuto: "Auto-captured",
97564
+ sourceManual: "Added manually",
97565
+ colUser: "USER",
97566
+ colMemory: "MEMORY",
97567
+ colAgent: "AGENT",
97568
+ colCategory: "CATEGORY",
97569
+ colCaptured: "CAPTURED"
97570
+ };
97540
97571
  var analyticsAnalyticsMonetizationStats$3 = {
97541
97572
  totalRevenue: "Total Revenue",
97542
97573
  totalSales: "Total Sales",
@@ -97782,6 +97813,13 @@ var analyticsStatCard$3 = {
97782
97813
  ariaLabel: "{title} mini card",
97783
97814
  ariaLabelValue: "{title} mini card value"
97784
97815
  };
97816
+ var analyticsUserFilterSelect$3 = {
97817
+ filterByUser: "Filter by user",
97818
+ you: "You",
97819
+ searchPlaceholder: "Search users…",
97820
+ noUsersFound: "No users found.",
97821
+ loadingUsers: "Loading users…"
97822
+ };
97785
97823
  var apiTabApiKeyModal$3 = {
97786
97824
  title: "API Key",
97787
97825
  dialogDescription: "Copy and paste the API key.",
@@ -99485,12 +99523,14 @@ var memoryMemoryTab$3 = {
99485
99523
  useMemoryLabelDesc: "When enabled, the AI will use what it knows about you to personalize responses.",
99486
99524
  useMemoryAriaLabel: "Use memory in responses {state}",
99487
99525
  sectionMyMemories: "My Memories",
99526
+ sectionMyMemoriesPreview: "Memories",
99488
99527
  addMemoryButton: "Add Memory",
99489
99528
  autoGeneratedMemoryAriaLabel: "Auto-generated memory",
99490
99529
  manuallyAddedMemoryAriaLabel: "Manually added memory",
99491
99530
  autoBadge: "auto",
99492
99531
  deleteMemoryAriaLabel: "Delete memory: {excerpt}",
99493
99532
  noMemoriesYet: "No memories yet. The AI will start remembering information from your conversations, or you can add memories manually.",
99533
+ noMemoriesYetPreview: "No memories yet. The AI has not recorded any information from this user's conversations.",
99494
99534
  stateEnabled: "enabled",
99495
99535
  stateDisabled: "disabled"
99496
99536
  };
@@ -101696,6 +101736,10 @@ var analyticsTabLabels$3 = {
101696
101736
  label: "Transcripts",
101697
101737
  description: "Read full conversation transcripts message by message."
101698
101738
  },
101739
+ memory: {
101740
+ label: "Memory",
101741
+ description: "What this agent remembers about each user it talks to."
101742
+ },
101699
101743
  financial: {
101700
101744
  label: "Costs",
101701
101745
  description: "Track token usage and what this agent is costing you."
@@ -102688,11 +102732,12 @@ var graderTabLabels$3 = {
102688
102732
  offHint: "Turn it on to write grading instructions and build your rubric. Anything you set up here is kept safe if you later turn it off."
102689
102733
  },
102690
102734
  subTabs: {
102691
- setup: "Grading setup",
102692
- rubric: "Rubric"
102735
+ setup: "Grading Setup",
102736
+ rubric: "Rubric",
102737
+ results: "Results"
102693
102738
  },
102694
102739
  denied: {
102695
- title: "No access to grading settings",
102740
+ title: "No Access to Grading Settings",
102696
102741
  description: "You don't have permission to view this agent's grading setup. Ask an administrator for access."
102697
102742
  },
102698
102743
  warnings: {
@@ -102700,12 +102745,12 @@ var graderTabLabels$3 = {
102700
102745
  noCriteria: "Grading is on but the rubric is empty — the agent will fail when asked to grade. Add at least one criterion in the Rubric tab."
102701
102746
  },
102702
102747
  config: {
102703
- sectionTitle: "Grading setup",
102748
+ sectionTitle: "Grading Setup",
102704
102749
  gradingMode: {
102705
- label: "What gets graded",
102750
+ label: "What Gets Graded",
102706
102751
  options: {
102707
- submission: "A submission",
102708
- conversation: "The conversation"
102752
+ submission: "A Submission",
102753
+ conversation: "The Conversation"
102709
102754
  },
102710
102755
  help: {
102711
102756
  submission: "The agent grades a piece of work the person hands in, like an essay or answer.",
@@ -102713,16 +102758,16 @@ var graderTabLabels$3 = {
102713
102758
  }
102714
102759
  },
102715
102760
  feedbackMode: {
102716
- label: "Feedback shared with the person",
102761
+ label: "Feedback Shared with the Person",
102717
102762
  options: {
102718
- overall: "Overall feedback only",
102719
- perCriteria: "Feedback per criterion",
102720
- both: "Overall + per criterion"
102763
+ overall: "Overall Feedback Only",
102764
+ perCriteria: "Feedback per Criterion",
102765
+ both: "Overall + per Criterion"
102721
102766
  },
102722
102767
  help: "The overall score is always calculated — this only controls how much detail is shared back."
102723
102768
  },
102724
102769
  instructions: {
102725
- label: "Grading instructions",
102770
+ label: "Grading Instructions",
102726
102771
  placeholder: "Describe what's being graded and anything the grader should know beyond the rubric. E.g. 'You are grading a 500-word persuasive essay on climate policy written by a high-school student. Be encouraging but honest.'",
102727
102772
  help: "Required. This is the context the agent uses every time it grades."
102728
102773
  },
@@ -102734,7 +102779,7 @@ var graderTabLabels$3 = {
102734
102779
  description: "Each row is one thing the agent looks for and how many points it is worth. The overall score is points earned out of the total.",
102735
102780
  needsConfigHint: "Save your grading setup first, then add rubric criteria here.",
102736
102781
  emptyState: "No criteria yet. Add your first one — for example \"Clarity\", 10 points.",
102737
- addButton: "Add criterion",
102782
+ addButton: "Add Criterion",
102738
102783
  columns: {
102739
102784
  name: "Name",
102740
102785
  criteria: "Criteria",
@@ -102743,12 +102788,12 @@ var graderTabLabels$3 = {
102743
102788
  actionsAria: "Actions for {name}",
102744
102789
  edit: "Edit",
102745
102790
  "delete": "Delete",
102746
- totalPoints: "Total possible points: {total}",
102791
+ totalPoints: "Total Possible Points: {total}",
102747
102792
  scoreHint: "Overall score = points earned ÷ {total}.",
102748
102793
  lastCriterionHint: "A rubric needs at least one criterion — add another before removing this one.",
102749
102794
  modal: {
102750
- addTitle: "Add criterion",
102751
- editTitle: "Edit criterion",
102795
+ addTitle: "Add Criterion",
102796
+ editTitle: "Edit Criterion",
102752
102797
  name: {
102753
102798
  label: "Name",
102754
102799
  placeholder: "e.g. Clarity",
@@ -102769,7 +102814,7 @@ var graderTabLabels$3 = {
102769
102814
  cancel: "Cancel"
102770
102815
  },
102771
102816
  deleteModal: {
102772
- title: "Delete criterion",
102817
+ title: "Delete Criterion",
102773
102818
  confirmationPrefix: "Are you sure you want to remove",
102774
102819
  confirmationSuffix: " from the rubric?",
102775
102820
  cancel: "Cancel",
@@ -102787,6 +102832,59 @@ var graderTabLabels$3 = {
102787
102832
  criterionUpdated: "Criterion updated",
102788
102833
  criterionDeleted: "Criterion deleted",
102789
102834
  criterionError: "Couldn't update the rubric"
102835
+ },
102836
+ results: {
102837
+ sectionTitle: "Grade Results",
102838
+ description: "Every grade this agent has issued. Override a score to correct it — the change is pushed back to the LMS the grade came from.",
102839
+ filters: {
102840
+ searchUser: "Search for User",
102841
+ searchUsersPlaceholder: "Search users...",
102842
+ noUsersFound: "No users found.",
102843
+ allUsers: "All Users",
102844
+ statusLabel: "Score Status",
102845
+ allStatuses: "All Statuses",
102846
+ dateRangeLabel: "Pick a Date Range"
102847
+ },
102848
+ statusValues: {
102849
+ pending: "Pending",
102850
+ published: "Published",
102851
+ failed: "Failed"
102852
+ },
102853
+ columns: {
102854
+ learner: "Learner",
102855
+ score: "Score",
102856
+ status: "Status",
102857
+ override: "Override",
102858
+ gradedAt: "Graded"
102859
+ },
102860
+ emptyState: "No grades yet. They will appear here once the agent starts grading.",
102861
+ emptyFiltered: "No grades match the current filters.",
102862
+ overriddenBadge: "Overridden",
102863
+ overrideModal: {
102864
+ title: "Override Grade",
102865
+ learnerLabel: "Learner",
102866
+ aiScoreLabel: "AI Score",
102867
+ currentOverrideLabel: "Current Override",
102868
+ noOverride: "None",
102869
+ pointsLabel: "Override Points",
102870
+ pointsHelp: "Between 0 and {total}. The score pushed to the LMS becomes points ÷ {total}.",
102871
+ pointsRange: "Points must be between 0 and {total}",
102872
+ feedbackLabel: "Override Feedback",
102873
+ feedbackPlaceholder: "Explain the change for the learner (optional).",
102874
+ clearHelp: "Clearing the override restores the AI score and pushes it back to the LMS.",
102875
+ save: "Save",
102876
+ saving: "Saving…",
102877
+ clear: "Clear",
102878
+ clearing: "Clearing…",
102879
+ cancel: "Cancel"
102880
+ },
102881
+ toasts: {
102882
+ overrideSaved: "Override saved and pushed to the LMS",
102883
+ overrideCleared: "Override cleared — the AI score was restored",
102884
+ overrideError: "Couldn't save the override"
102885
+ },
102886
+ overrideButton: "Override",
102887
+ overrideButtonAria: "Override grade for {email}"
102790
102888
  }
102791
102889
  };
102792
102890
  var en = {
@@ -102815,6 +102913,7 @@ var en = {
102815
102913
  analyticsAnalyticsCourseDetail: analyticsAnalyticsCourseDetail$3,
102816
102914
  analyticsAnalyticsCourses: analyticsAnalyticsCourses$3,
102817
102915
  analyticsAnalyticsFinancialStats: analyticsAnalyticsFinancialStats$3,
102916
+ analyticsAnalyticsMemoryStats: analyticsAnalyticsMemoryStats$3,
102818
102917
  analyticsAnalyticsMonetizationStats: analyticsAnalyticsMonetizationStats$3,
102819
102918
  analyticsAnalyticsOverview: analyticsAnalyticsOverview$3,
102820
102919
  analyticsAnalyticsProgramDetail: analyticsAnalyticsProgramDetail$3,
@@ -102825,6 +102924,7 @@ var en = {
102825
102924
  analyticsAnalyticsUsersStats: analyticsAnalyticsUsersStats$3,
102826
102925
  analyticsGroupsFilterDropdown: analyticsGroupsFilterDropdown$3,
102827
102926
  analyticsStatCard: analyticsStatCard$3,
102927
+ analyticsUserFilterSelect: analyticsUserFilterSelect$3,
102828
102928
  apiTabApiKeyModal: apiTabApiKeyModal$3,
102829
102929
  apiTabCreateApiModal2: apiTabCreateApiModal2$3,
102830
102930
  apiTabDeleteApiModal2: apiTabDeleteApiModal2$3,
@@ -103757,6 +103857,37 @@ var analyticsAnalyticsFinancialStats$2 = {
103757
103857
  goToLastPage: "Aller à la dernière page",
103758
103858
  paginationShowing: "Affichage de {from} à {to} sur {total} résultats"
103759
103859
  };
103860
+ var analyticsAnalyticsMemoryStats$2 = {
103861
+ noPermission: "Vous n'avez pas l'autorisation de consulter les mémoires des utilisateurs.",
103862
+ scopeLabel: "Portée de la mémoire",
103863
+ scopeMentor: "Cet agent",
103864
+ scopeAllMentors: "Tous les agents",
103865
+ scopeGlobal: "Global",
103866
+ searchAgents: "Rechercher des agents…",
103867
+ searchAgentsTitle: "Rechercher des agents",
103868
+ clearAgentSearch: "Effacer la recherche d'agent",
103869
+ contentPlaceholder: "Rechercher dans le contenu des mémoires",
103870
+ pickDateRange: "Choisir une plage de dates",
103871
+ allCategories: "Toutes les catégories",
103872
+ byCategory: "Mémoires par catégorie",
103873
+ categoryCount: "{count} ({percent}%)",
103874
+ loadError: "Une erreur s'est produite lors du chargement des mémoires.",
103875
+ noMemories: "Aucune mémoire n'a encore été capturée.",
103876
+ detailTitle: "Détail de la mémoire",
103877
+ detailUser: "Utilisateur",
103878
+ detailAgent: "Agent",
103879
+ detailCategory: "Catégorie",
103880
+ detailSource: "Source",
103881
+ detailCaptured: "Capturée",
103882
+ detailSession: "Session",
103883
+ sourceAuto: "Capturée automatiquement",
103884
+ sourceManual: "Ajoutée manuellement",
103885
+ colUser: "UTILISATEUR",
103886
+ colMemory: "MÉMOIRE",
103887
+ colAgent: "AGENT",
103888
+ colCategory: "CATÉGORIE",
103889
+ colCaptured: "CAPTURÉE"
103890
+ };
103760
103891
  var analyticsAnalyticsMonetizationStats$2 = {
103761
103892
  totalRevenue: "Revenu total",
103762
103893
  totalSales: "Ventes totales",
@@ -104002,6 +104133,13 @@ var analyticsStatCard$2 = {
104002
104133
  ariaLabel: "mini carte {title}",
104003
104134
  ariaLabelValue: "valeur de la mini carte {title}"
104004
104135
  };
104136
+ var analyticsUserFilterSelect$2 = {
104137
+ filterByUser: "Filtrer par utilisateur",
104138
+ you: "Vous",
104139
+ searchPlaceholder: "Rechercher des utilisateurs…",
104140
+ noUsersFound: "Aucun utilisateur trouvé.",
104141
+ loadingUsers: "Chargement des utilisateurs…"
104142
+ };
104005
104143
  var apiTabApiKeyModal$2 = {
104006
104144
  title: "Clé API",
104007
104145
  dialogDescription: "Copiez et collez la clé API.",
@@ -104329,7 +104467,8 @@ var graderTabLabels$2 = {
104329
104467
  },
104330
104468
  subTabs: {
104331
104469
  setup: "Configuration de la notation",
104332
- rubric: "Grille d'évaluation"
104470
+ rubric: "Grille d'évaluation",
104471
+ results: "Résultats"
104333
104472
  },
104334
104473
  denied: {
104335
104474
  title: "Pas d'accès aux paramètres de notation",
@@ -104417,6 +104556,59 @@ var graderTabLabels$2 = {
104417
104556
  deleting: "Suppression…"
104418
104557
  }
104419
104558
  },
104559
+ results: {
104560
+ sectionTitle: "Résultats de notation",
104561
+ description: "Toutes les notes émises par cet agent. Remplacez une note pour la corriger — la modification est renvoyée au LMS d'où provient la note.",
104562
+ filters: {
104563
+ searchUser: "Rechercher un utilisateur",
104564
+ searchUsersPlaceholder: "Rechercher des utilisateurs...",
104565
+ noUsersFound: "Aucun utilisateur trouvé.",
104566
+ allUsers: "Tous les utilisateurs",
104567
+ statusLabel: "Statut de la note",
104568
+ allStatuses: "Tous les statuts",
104569
+ dateRangeLabel: "Choisir une plage de dates"
104570
+ },
104571
+ statusValues: {
104572
+ pending: "En attente",
104573
+ published: "Publié",
104574
+ failed: "Échoué"
104575
+ },
104576
+ columns: {
104577
+ learner: "Apprenant",
104578
+ score: "Note",
104579
+ status: "Statut",
104580
+ override: "Remplacement",
104581
+ gradedAt: "Noté"
104582
+ },
104583
+ emptyState: "Aucune note pour l'instant. Elles apparaîtront ici dès que l'agent commencera à noter.",
104584
+ emptyFiltered: "Aucune note ne correspond aux filtres actuels.",
104585
+ overriddenBadge: "Remplacée",
104586
+ overrideModal: {
104587
+ title: "Remplacer la note",
104588
+ learnerLabel: "Apprenant",
104589
+ aiScoreLabel: "Note de l'IA",
104590
+ currentOverrideLabel: "Remplacement actuel",
104591
+ noOverride: "Aucun",
104592
+ pointsLabel: "Points de remplacement",
104593
+ pointsHelp: "Entre 0 et {total}. La note envoyée au LMS devient points ÷ {total}.",
104594
+ pointsRange: "Les points doivent être compris entre 0 et {total}",
104595
+ feedbackLabel: "Retour sur le remplacement",
104596
+ feedbackPlaceholder: "Expliquez le changement à l'apprenant (facultatif).",
104597
+ clearHelp: "Supprimer le remplacement restaure la note de l'IA et la renvoie au LMS.",
104598
+ save: "Enregistrer",
104599
+ saving: "Enregistrement…",
104600
+ clear: "Supprimer",
104601
+ clearing: "Suppression…",
104602
+ cancel: "Annuler"
104603
+ },
104604
+ toasts: {
104605
+ overrideSaved: "Remplacement enregistré et envoyé au LMS",
104606
+ overrideCleared: "Remplacement supprimé — la note de l'IA a été restaurée",
104607
+ overrideError: "Impossible d'enregistrer le remplacement"
104608
+ },
104609
+ overrideButton: "Remplacer",
104610
+ overrideButtonAria: "Remplacer la note de {email}"
104611
+ },
104420
104612
  toasts: {
104421
104613
  toggleOn: "Notation activée",
104422
104614
  toggleOff: "Notation désactivée — votre grille est conservée pour la prochaine fois",
@@ -105817,12 +106009,14 @@ var memoryMemoryTab$2 = {
105817
106009
  useMemoryLabelDesc: "Lorsque cette option est activée, l'IA utilisera ce qu'elle sait de vous pour personnaliser ses réponses.",
105818
106010
  useMemoryAriaLabel: "Utilisation de la mémoire dans les réponses {state}",
105819
106011
  sectionMyMemories: "Mes souvenirs",
106012
+ sectionMyMemoriesPreview: "Souvenirs",
105820
106013
  addMemoryButton: "Ajouter un souvenir",
105821
106014
  autoGeneratedMemoryAriaLabel: "Souvenir généré automatiquement",
105822
106015
  manuallyAddedMemoryAriaLabel: "Souvenir ajouté manuellement",
105823
106016
  autoBadge: "auto",
105824
106017
  deleteMemoryAriaLabel: "Supprimer le souvenir : {excerpt}",
105825
106018
  noMemoriesYet: "Aucun souvenir pour l'instant. L'IA commencera à mémoriser des informations de vos conversations, ou vous pouvez ajouter des souvenirs manuellement.",
106019
+ noMemoriesYetPreview: "Aucun souvenir pour l'instant. L'IA n'a enregistré aucune information des conversations de cet utilisateur.",
105826
106020
  stateEnabled: "activée",
105827
106021
  stateDisabled: "désactivée"
105828
106022
  };
@@ -108028,6 +108222,10 @@ var analyticsTabLabels$2 = {
108028
108222
  label: "Transcriptions",
108029
108223
  description: "Lisez les transcriptions complètes des conversations, message par message."
108030
108224
  },
108225
+ memory: {
108226
+ label: "Mémoire",
108227
+ description: "Ce que cet agent retient de chaque utilisateur avec qui il échange."
108228
+ },
108031
108229
  financial: {
108032
108230
  label: "Coûts",
108033
108231
  description: "Suivez l'utilisation des jetons et ce que cet agent vous coûte."
@@ -109035,6 +109233,7 @@ var fr = {
109035
109233
  analyticsAnalyticsCourseDetail: analyticsAnalyticsCourseDetail$2,
109036
109234
  analyticsAnalyticsCourses: analyticsAnalyticsCourses$2,
109037
109235
  analyticsAnalyticsFinancialStats: analyticsAnalyticsFinancialStats$2,
109236
+ analyticsAnalyticsMemoryStats: analyticsAnalyticsMemoryStats$2,
109038
109237
  analyticsAnalyticsMonetizationStats: analyticsAnalyticsMonetizationStats$2,
109039
109238
  analyticsAnalyticsOverview: analyticsAnalyticsOverview$2,
109040
109239
  analyticsAnalyticsProgramDetail: analyticsAnalyticsProgramDetail$2,
@@ -109045,6 +109244,7 @@ var fr = {
109045
109244
  analyticsAnalyticsUsersStats: analyticsAnalyticsUsersStats$2,
109046
109245
  analyticsGroupsFilterDropdown: analyticsGroupsFilterDropdown$2,
109047
109246
  analyticsStatCard: analyticsStatCard$2,
109247
+ analyticsUserFilterSelect: analyticsUserFilterSelect$2,
109048
109248
  apiTabApiKeyModal: apiTabApiKeyModal$2,
109049
109249
  apiTabCreateApiModal2: apiTabCreateApiModal2$2,
109050
109250
  apiTabDeleteApiModal2: apiTabDeleteApiModal2$2,
@@ -109977,6 +110177,37 @@ var analyticsAnalyticsFinancialStats$1 = {
109977
110177
  goToLastPage: "Ir a la última página",
109978
110178
  paginationShowing: "Mostrando {from} a {to} de {total} resultados"
109979
110179
  };
110180
+ var analyticsAnalyticsMemoryStats$1 = {
110181
+ noPermission: "No tienes permiso para ver los recuerdos de los usuarios.",
110182
+ scopeLabel: "Alcance de la memoria",
110183
+ scopeMentor: "Este agente",
110184
+ scopeAllMentors: "Todos los agentes",
110185
+ scopeGlobal: "Global",
110186
+ searchAgents: "Buscar agentes…",
110187
+ searchAgentsTitle: "Buscar agentes",
110188
+ clearAgentSearch: "Borrar la búsqueda de agentes",
110189
+ contentPlaceholder: "Buscar en el contenido de los recuerdos",
110190
+ pickDateRange: "Elige un rango de fechas",
110191
+ allCategories: "Todas las categorías",
110192
+ byCategory: "Recuerdos por categoría",
110193
+ categoryCount: "{count} ({percent}%)",
110194
+ loadError: "Se produjo un error al cargar los recuerdos.",
110195
+ noMemories: "Todavía no se ha capturado ningún recuerdo.",
110196
+ detailTitle: "Detalle del recuerdo",
110197
+ detailUser: "Usuario",
110198
+ detailAgent: "Agente",
110199
+ detailCategory: "Categoría",
110200
+ detailSource: "Origen",
110201
+ detailCaptured: "Capturado",
110202
+ detailSession: "Sesión",
110203
+ sourceAuto: "Capturado automáticamente",
110204
+ sourceManual: "Añadido manualmente",
110205
+ colUser: "USUARIO",
110206
+ colMemory: "RECUERDO",
110207
+ colAgent: "AGENTE",
110208
+ colCategory: "CATEGORÍA",
110209
+ colCaptured: "CAPTURADO"
110210
+ };
109980
110211
  var analyticsAnalyticsMonetizationStats$1 = {
109981
110212
  totalRevenue: "Ingresos totales",
109982
110213
  totalSales: "Ventas totales",
@@ -110222,6 +110453,13 @@ var analyticsStatCard$1 = {
110222
110453
  ariaLabel: "mini tarjeta {title}",
110223
110454
  ariaLabelValue: "valor de la mini tarjeta {title}"
110224
110455
  };
110456
+ var analyticsUserFilterSelect$1 = {
110457
+ filterByUser: "Filtrar por usuario",
110458
+ you: "Tú",
110459
+ searchPlaceholder: "Buscar usuarios…",
110460
+ noUsersFound: "No se encontraron usuarios.",
110461
+ loadingUsers: "Cargando usuarios…"
110462
+ };
110225
110463
  var apiTabApiKeyModal$1 = {
110226
110464
  title: "Clave API",
110227
110465
  dialogDescription: "Copie y pegue la clave API.",
@@ -110549,7 +110787,8 @@ var graderTabLabels$1 = {
110549
110787
  },
110550
110788
  subTabs: {
110551
110789
  setup: "Configuración de calificación",
110552
- rubric: "Rúbrica"
110790
+ rubric: "Rúbrica",
110791
+ results: "Resultados"
110553
110792
  },
110554
110793
  denied: {
110555
110794
  title: "Sin acceso a la configuración de calificación",
@@ -110637,6 +110876,59 @@ var graderTabLabels$1 = {
110637
110876
  deleting: "Eliminando…"
110638
110877
  }
110639
110878
  },
110879
+ results: {
110880
+ sectionTitle: "Resultados de calificación",
110881
+ description: "Todas las calificaciones que este agente ha emitido. Anula una puntuación para corregirla — el cambio se envía de vuelta al LMS del que proviene la calificación.",
110882
+ filters: {
110883
+ searchUser: "Buscar usuario",
110884
+ searchUsersPlaceholder: "Buscar usuarios...",
110885
+ noUsersFound: "No se encontraron usuarios.",
110886
+ allUsers: "Todos los usuarios",
110887
+ statusLabel: "Estado de la puntuación",
110888
+ allStatuses: "Todos los estados",
110889
+ dateRangeLabel: "Selecciona un rango de fechas"
110890
+ },
110891
+ statusValues: {
110892
+ pending: "Pendiente",
110893
+ published: "Publicado",
110894
+ failed: "Fallido"
110895
+ },
110896
+ columns: {
110897
+ learner: "Estudiante",
110898
+ score: "Puntuación",
110899
+ status: "Estado",
110900
+ override: "Anulación",
110901
+ gradedAt: "Calificado"
110902
+ },
110903
+ emptyState: "Aún no hay calificaciones. Aparecerán aquí cuando el agente comience a calificar.",
110904
+ emptyFiltered: "Ninguna calificación coincide con los filtros actuales.",
110905
+ overriddenBadge: "Anulada",
110906
+ overrideModal: {
110907
+ title: "Anular calificación",
110908
+ learnerLabel: "Estudiante",
110909
+ aiScoreLabel: "Puntuación de la IA",
110910
+ currentOverrideLabel: "Anulación actual",
110911
+ noOverride: "Ninguna",
110912
+ pointsLabel: "Puntos de anulación",
110913
+ pointsHelp: "Entre 0 y {total}. La puntuación enviada al LMS es puntos ÷ {total}.",
110914
+ pointsRange: "Los puntos deben estar entre 0 y {total}",
110915
+ feedbackLabel: "Retroalimentación de la anulación",
110916
+ feedbackPlaceholder: "Explica el cambio al estudiante (opcional).",
110917
+ clearHelp: "Quitar la anulación restaura la puntuación de la IA y la envía de vuelta al LMS.",
110918
+ save: "Guardar",
110919
+ saving: "Guardando…",
110920
+ clear: "Quitar",
110921
+ clearing: "Quitando…",
110922
+ cancel: "Cancelar"
110923
+ },
110924
+ toasts: {
110925
+ overrideSaved: "Anulación guardada y enviada al LMS",
110926
+ overrideCleared: "Anulación quitada — se restauró la puntuación de la IA",
110927
+ overrideError: "No se pudo guardar la anulación"
110928
+ },
110929
+ overrideButton: "Anular",
110930
+ overrideButtonAria: "Anular la calificación de {email}"
110931
+ },
110640
110932
  toasts: {
110641
110933
  toggleOn: "Calificación activada",
110642
110934
  toggleOff: "Calificación desactivada — tu rúbrica se conserva para la próxima vez",
@@ -112037,12 +112329,14 @@ var memoryMemoryTab$1 = {
112037
112329
  useMemoryLabelDesc: "Cuando está habilitado, la IA usará lo que sabe sobre usted para personalizar las respuestas.",
112038
112330
  useMemoryAriaLabel: "Uso de memoria en las respuestas {state}",
112039
112331
  sectionMyMemories: "Mis memorias",
112332
+ sectionMyMemoriesPreview: "Memorias",
112040
112333
  addMemoryButton: "Añadir memoria",
112041
112334
  autoGeneratedMemoryAriaLabel: "Memoria generada automáticamente",
112042
112335
  manuallyAddedMemoryAriaLabel: "Memoria añadida manualmente",
112043
112336
  autoBadge: "auto",
112044
112337
  deleteMemoryAriaLabel: "Eliminar memoria: {excerpt}",
112045
112338
  noMemoriesYet: "Aún no hay memorias. La IA comenzará a recordar información de sus conversaciones, o puede añadir memorias manualmente.",
112339
+ noMemoriesYetPreview: "Aún no hay memorias. La IA no ha registrado información de las conversaciones de este usuario.",
112046
112340
  stateEnabled: "habilitada",
112047
112341
  stateDisabled: "deshabilitada"
112048
112342
  };
@@ -114248,6 +114542,10 @@ var analyticsTabLabels$1 = {
114248
114542
  label: "Transcripciones",
114249
114543
  description: "Lee las transcripciones completas de las conversaciones, mensaje a mensaje."
114250
114544
  },
114545
+ memory: {
114546
+ label: "Memoria",
114547
+ description: "Lo que este agente recuerda de cada usuario con el que habla."
114548
+ },
114251
114549
  financial: {
114252
114550
  label: "Costos",
114253
114551
  description: "Controla el uso de tokens y lo que te cuesta este agente."
@@ -115255,6 +115553,7 @@ var es = {
115255
115553
  analyticsAnalyticsCourseDetail: analyticsAnalyticsCourseDetail$1,
115256
115554
  analyticsAnalyticsCourses: analyticsAnalyticsCourses$1,
115257
115555
  analyticsAnalyticsFinancialStats: analyticsAnalyticsFinancialStats$1,
115556
+ analyticsAnalyticsMemoryStats: analyticsAnalyticsMemoryStats$1,
115258
115557
  analyticsAnalyticsMonetizationStats: analyticsAnalyticsMonetizationStats$1,
115259
115558
  analyticsAnalyticsOverview: analyticsAnalyticsOverview$1,
115260
115559
  analyticsAnalyticsProgramDetail: analyticsAnalyticsProgramDetail$1,
@@ -115265,6 +115564,7 @@ var es = {
115265
115564
  analyticsAnalyticsUsersStats: analyticsAnalyticsUsersStats$1,
115266
115565
  analyticsGroupsFilterDropdown: analyticsGroupsFilterDropdown$1,
115267
115566
  analyticsStatCard: analyticsStatCard$1,
115567
+ analyticsUserFilterSelect: analyticsUserFilterSelect$1,
115268
115568
  apiTabApiKeyModal: apiTabApiKeyModal$1,
115269
115569
  apiTabCreateApiModal2: apiTabCreateApiModal2$1,
115270
115570
  apiTabDeleteApiModal2: apiTabDeleteApiModal2$1,
@@ -116197,6 +116497,37 @@ var analyticsAnalyticsFinancialStats = {
116197
116497
  goToLastPage: "转到最后一页",
116198
116498
  paginationShowing: "显示第 {from} 至 {to} 条,共 {total} 条结果"
116199
116499
  };
116500
+ var analyticsAnalyticsMemoryStats = {
116501
+ noPermission: "您没有查看用户记忆的权限。",
116502
+ scopeLabel: "记忆范围",
116503
+ scopeMentor: "当前智能体",
116504
+ scopeAllMentors: "所有智能体",
116505
+ scopeGlobal: "全局",
116506
+ searchAgents: "搜索智能体…",
116507
+ searchAgentsTitle: "搜索智能体",
116508
+ clearAgentSearch: "清除智能体搜索",
116509
+ contentPlaceholder: "搜索记忆内容",
116510
+ pickDateRange: "选择日期范围",
116511
+ allCategories: "所有类别",
116512
+ byCategory: "按类别统计记忆",
116513
+ categoryCount: "{count} ({percent}%)",
116514
+ loadError: "加载记忆时发生错误。",
116515
+ noMemories: "尚未捕获任何记忆。",
116516
+ detailTitle: "记忆详情",
116517
+ detailUser: "用户",
116518
+ detailAgent: "智能体",
116519
+ detailCategory: "类别",
116520
+ detailSource: "来源",
116521
+ detailCaptured: "捕获时间",
116522
+ detailSession: "会话",
116523
+ sourceAuto: "自动捕获",
116524
+ sourceManual: "手动添加",
116525
+ colUser: "用户",
116526
+ colMemory: "记忆",
116527
+ colAgent: "智能体",
116528
+ colCategory: "类别",
116529
+ colCaptured: "捕获时间"
116530
+ };
116200
116531
  var analyticsAnalyticsMonetizationStats = {
116201
116532
  totalRevenue: "总收入",
116202
116533
  totalSales: "总销售额",
@@ -116442,6 +116773,13 @@ var analyticsStatCard = {
116442
116773
  ariaLabel: "{title} 迷你卡片",
116443
116774
  ariaLabelValue: "{title} 迷你卡片值"
116444
116775
  };
116776
+ var analyticsUserFilterSelect = {
116777
+ filterByUser: "按用户筛选",
116778
+ you: "我",
116779
+ searchPlaceholder: "搜索用户…",
116780
+ noUsersFound: "未找到用户。",
116781
+ loadingUsers: "正在加载用户…"
116782
+ };
116445
116783
  var apiTabApiKeyModal = {
116446
116784
  title: "API 密钥",
116447
116785
  dialogDescription: "复制并粘贴 API 密钥。",
@@ -116769,7 +117107,8 @@ var graderTabLabels = {
116769
117107
  },
116770
117108
  subTabs: {
116771
117109
  setup: "评分设置",
116772
- rubric: "评分标准"
117110
+ rubric: "评分标准",
117111
+ results: "结果"
116773
117112
  },
116774
117113
  denied: {
116775
117114
  title: "无权访问评分设置",
@@ -116857,6 +117196,59 @@ var graderTabLabels = {
116857
117196
  deleting: "删除中…"
116858
117197
  }
116859
117198
  },
117199
+ results: {
117200
+ sectionTitle: "评分结果",
117201
+ description: "此智能体发出的所有评分。覆盖分数即可更正 — 更改会推送回评分来源的 LMS。",
117202
+ filters: {
117203
+ searchUser: "搜索用户",
117204
+ searchUsersPlaceholder: "搜索用户...",
117205
+ noUsersFound: "未找到用户。",
117206
+ allUsers: "所有用户",
117207
+ statusLabel: "分数状态",
117208
+ allStatuses: "全部状态",
117209
+ dateRangeLabel: "选择日期范围"
117210
+ },
117211
+ statusValues: {
117212
+ pending: "待处理",
117213
+ published: "已发布",
117214
+ failed: "失败"
117215
+ },
117216
+ columns: {
117217
+ learner: "学员",
117218
+ score: "分数",
117219
+ status: "状态",
117220
+ override: "覆盖",
117221
+ gradedAt: "评分时间"
117222
+ },
117223
+ emptyState: "还没有评分。智能体开始评分后将显示在这里。",
117224
+ emptyFiltered: "没有符合当前筛选条件的评分。",
117225
+ overriddenBadge: "已覆盖",
117226
+ overrideModal: {
117227
+ title: "覆盖评分",
117228
+ learnerLabel: "学员",
117229
+ aiScoreLabel: "AI 分数",
117230
+ currentOverrideLabel: "当前覆盖",
117231
+ noOverride: "无",
117232
+ pointsLabel: "覆盖分数",
117233
+ pointsHelp: "介于 0 和 {total} 之间。推送到 LMS 的分数为 分数 ÷ {total}。",
117234
+ pointsRange: "分数必须介于 0 和 {total} 之间",
117235
+ feedbackLabel: "覆盖反馈",
117236
+ feedbackPlaceholder: "向学员说明此更改(可选)。",
117237
+ clearHelp: "清除覆盖会恢复 AI 分数并推送回 LMS。",
117238
+ save: "保存",
117239
+ saving: "保存中…",
117240
+ clear: "清除",
117241
+ clearing: "清除中…",
117242
+ cancel: "取消"
117243
+ },
117244
+ toasts: {
117245
+ overrideSaved: "覆盖已保存并推送到 LMS",
117246
+ overrideCleared: "覆盖已清除 — AI 分数已恢复",
117247
+ overrideError: "无法保存覆盖"
117248
+ },
117249
+ overrideButton: "覆盖",
117250
+ overrideButtonAria: "覆盖 {email} 的评分"
117251
+ },
116860
117252
  toasts: {
116861
117253
  toggleOn: "评分已开启",
116862
117254
  toggleOff: "评分已关闭 — 您的评分标准已保留,下次可继续使用",
@@ -118257,12 +118649,14 @@ var memoryMemoryTab = {
118257
118649
  useMemoryLabelDesc: "启用后,AI 将利用它对您的了解来个性化回复。",
118258
118650
  useMemoryAriaLabel: "在回复中使用记忆{state}",
118259
118651
  sectionMyMemories: "我的记忆",
118652
+ sectionMyMemoriesPreview: "记忆",
118260
118653
  addMemoryButton: "添加记忆",
118261
118654
  autoGeneratedMemoryAriaLabel: "自动生成的记忆",
118262
118655
  manuallyAddedMemoryAriaLabel: "手动添加的记忆",
118263
118656
  autoBadge: "自动",
118264
118657
  deleteMemoryAriaLabel: "删除记忆:{excerpt}",
118265
118658
  noMemoriesYet: "暂无记忆。AI 将开始记住您对话中的信息,或者您也可以手动添加记忆。",
118659
+ noMemoriesYetPreview: "暂无记忆。AI 尚未记录该用户对话中的任何信息。",
118266
118660
  stateEnabled: "已启用",
118267
118661
  stateDisabled: "已禁用"
118268
118662
  };
@@ -120468,6 +120862,10 @@ var analyticsTabLabels = {
120468
120862
  label: "对话记录",
120469
120863
  description: "逐条阅读完整的对话记录。"
120470
120864
  },
120865
+ memory: {
120866
+ label: "记忆",
120867
+ description: "该智能体记住的与其对话的每位用户的相关信息。"
120868
+ },
120471
120869
  financial: {
120472
120870
  label: "费用",
120473
120871
  description: "跟踪令牌用量以及该智能体的花费。"
@@ -121475,6 +121873,7 @@ var zh = {
121475
121873
  analyticsAnalyticsCourseDetail: analyticsAnalyticsCourseDetail,
121476
121874
  analyticsAnalyticsCourses: analyticsAnalyticsCourses,
121477
121875
  analyticsAnalyticsFinancialStats: analyticsAnalyticsFinancialStats,
121876
+ analyticsAnalyticsMemoryStats: analyticsAnalyticsMemoryStats,
121478
121877
  analyticsAnalyticsMonetizationStats: analyticsAnalyticsMonetizationStats,
121479
121878
  analyticsAnalyticsOverview: analyticsAnalyticsOverview,
121480
121879
  analyticsAnalyticsProgramDetail: analyticsAnalyticsProgramDetail,
@@ -121485,6 +121884,7 @@ var zh = {
121485
121884
  analyticsAnalyticsUsersStats: analyticsAnalyticsUsersStats,
121486
121885
  analyticsGroupsFilterDropdown: analyticsGroupsFilterDropdown,
121487
121886
  analyticsStatCard: analyticsStatCard,
121887
+ analyticsUserFilterSelect: analyticsUserFilterSelect,
121488
121888
  apiTabApiKeyModal: apiTabApiKeyModal,
121489
121889
  apiTabCreateApiModal2: apiTabCreateApiModal2,
121490
121890
  apiTabDeleteApiModal2: apiTabDeleteApiModal2,
@@ -124462,11 +124862,10 @@ const getUserName$1 = () => {
124462
124862
  };
124463
124863
 
124464
124864
  /**
124465
- * True when the logged-in user is viewing someone else's profile (preview
124466
- * mode), so editing affordances can be hidden. Reads the current username from
124467
- * localStorage after mount to stay SSR-safe.
124865
+ * The signed-in user's username, or an empty string before it is known. Read
124866
+ * from localStorage after mount to stay SSR-safe.
124468
124867
  */
124469
- function useIsProfilePreview(username) {
124868
+ function useCurrentUsername() {
124470
124869
  const [currentUsername, setCurrentUsername] = useState('');
124471
124870
  useEffect(() => {
124472
124871
  try {
@@ -124476,6 +124875,14 @@ function useIsProfilePreview(username) {
124476
124875
  setCurrentUsername('');
124477
124876
  }
124478
124877
  }, []);
124878
+ return currentUsername;
124879
+ }
124880
+ /**
124881
+ * True when the logged-in user is viewing someone else's profile (preview
124882
+ * mode), so editing affordances can be hidden.
124883
+ */
124884
+ function useIsProfilePreview(username) {
124885
+ const currentUsername = useCurrentUsername();
124479
124886
  return !!currentUsername && currentUsername !== username;
124480
124887
  }
124481
124888
 
@@ -175623,7 +176030,7 @@ function DetailRow({ label, children }) {
175623
176030
  return (jsxs("div", { className: "flex items-center justify-between gap-4 py-2", children: [jsx("span", { className: "text-sm text-gray-500", children: label }), jsx("span", { className: "text-sm font-medium text-gray-900 text-right", children: children })] }));
175624
176031
  }
175625
176032
 
175626
- const PAGE_SIZE$9 = 8;
176033
+ const PAGE_SIZE$b = 8;
175627
176034
  const CONTENT_TYPES = [
175628
176035
  { value: 'courses', labelKey: 'gradebookGradebookTab.typeCourses', icon: GraduationCap },
175629
176036
  { value: 'programs', labelKey: 'gradebookGradebookTab.typePrograms', icon: Layers },
@@ -175654,14 +176061,14 @@ function useGradebookData(type, org, username, page, search) {
175654
176061
  username,
175655
176062
  query: {
175656
176063
  page,
175657
- page_size: PAGE_SIZE$9,
176064
+ page_size: PAGE_SIZE$b,
175658
176065
  platform_key: org,
175659
176066
  ...(trimmed ? { search: trimmed } : {}),
175660
176067
  },
175661
176068
  }, { skip: type !== 'courses', refetchOnMountOrArgChange: true });
175662
176069
  const programsQ = useGetUserEnrolledProgramsQuery([{ username, platformKey: org }], { skip: type !== 'programs' });
175663
176070
  const pathwaysQ = useGetUserCatalogPathwaysQuery({ username, platform_key: org }, { skip: type !== 'pathways' });
175664
- const credentialsQ = useGetUsersAsAssertionsQuery({ platformKey: org, userId: username, page, pageSize: PAGE_SIZE$9 }, { skip: type !== 'credentials', refetchOnMountOrArgChange: true });
176071
+ const credentialsQ = useGetUsersAsAssertionsQuery({ platformKey: org, userId: username, page, pageSize: PAGE_SIZE$b }, { skip: type !== 'credentials', refetchOnMountOrArgChange: true });
175665
176072
  const earnedQ = useGetUserEarnedSkillsQuery([{ org, userId: username }], { skip: type !== 'skills' });
175666
176073
  const reportedQ = useGetUserReportedSkillsQuery([{ username }], { skip: type !== 'skills' });
175667
176074
  const desiredQ = useGetUserDesiredSkillsQuery([{ username }], { skip: type !== 'skills' });
@@ -175671,12 +176078,12 @@ function useGradebookData(type, org, username, page, search) {
175671
176078
  const filtered = trimmed
175672
176079
  ? rows.filter((row) => row.name.toLowerCase().includes(trimmed.toLowerCase()))
175673
176080
  : rows;
175674
- const start = (page - 1) * PAGE_SIZE$9;
176081
+ const start = (page - 1) * PAGE_SIZE$b;
175675
176082
  return {
175676
- rows: filtered.slice(start, start + PAGE_SIZE$9),
176083
+ rows: filtered.slice(start, start + PAGE_SIZE$b),
175677
176084
  total: filtered.length,
175678
176085
  hasPrev: page > 1,
175679
- hasNext: start + PAGE_SIZE$9 < filtered.length,
176086
+ hasNext: start + PAGE_SIZE$b < filtered.length,
175680
176087
  };
175681
176088
  };
175682
176089
  switch (type) {
@@ -175733,7 +176140,7 @@ function useGradebookData(type, org, username, page, search) {
175733
176140
  rows,
175734
176141
  total,
175735
176142
  hasPrev: page > 1,
175736
- hasNext: page * PAGE_SIZE$9 < total,
176143
+ hasNext: page * PAGE_SIZE$b < total,
175737
176144
  isLoading: credentialsQ.isLoading,
175738
176145
  isFetching: credentialsQ.isFetching,
175739
176146
  isError: credentialsQ.isError,
@@ -175972,29 +176379,58 @@ const IblPagination = ({ currentPage, totalPages, onPageChange, disabled = false
175972
176379
 
175973
176380
  const MEMORIES_PAGE_SIZE = 25;
175974
176381
  function MemoryTab({ org, username }) {
175975
- var _a, _b, _c;
176382
+ var _a, _b;
175976
176383
  const t = useT();
175977
176384
  const [isAddDialogOpen, setIsAddDialogOpen] = useState(false);
175978
176385
  const [deletingId, setDeletingId] = useState(null);
175979
176386
  const [currentPage, setCurrentPage] = useState(1);
176387
+ // Viewing someone else's profile, which lists both of that user's memory
176388
+ // collections — global and per-agent — instead of just your own global feed.
176389
+ const isPreview = useIsProfilePreview(username);
176390
+ const currentUsername = useCurrentUsername();
175980
176391
  // Platform memsearch status (available to students and admins)
175981
176392
  const { data: memsearchStatus, isLoading: isLoadingConfig, isError: isConfigError, } = useGetMemsearchStatusQuery({
175982
176393
  org,
175983
176394
  userId: username,
175984
- });
176395
+ }, { skip: isPreview });
175985
176396
  // User memory settings
175986
176397
  const { data: settings, isLoading: isLoadingSettings } = useGetUserMemorySettingsQuery({
175987
176398
  org,
175988
176399
  userId: username,
175989
- });
176400
+ }, { skip: isPreview });
175990
176401
  const [updateSettings, { isLoading: isUpdatingSettings }] = useUpdateUserMemorySettingsMutation();
175991
- // Global memories
175992
- const { data: memoriesData, isLoading: isLoadingMemories } = useGetGlobalMemoriesQuery({
176402
+ const memoriesParams = { page: currentPage, page_size: MEMORIES_PAGE_SIZE };
176403
+ // Global memories. On your own profile this is the plain call — your username
176404
+ // in the path. Previewing someone else addresses it the way the analytics
176405
+ // memory tab does: the caller in the `{user_id}` path segment, and the user
176406
+ // being looked at in the `user_id` param.
176407
+ const { data: globalMemoriesData, isLoading: isLoadingGlobalMemories } = useGetGlobalMemoriesQuery(isPreview
176408
+ ? { org, userId: currentUsername, params: { ...memoriesParams, user_id: username } }
176409
+ : { org, userId: username, params: memoriesParams }, { skip: isPreview && !currentUsername });
176410
+ // Agent memories across every mentor, addressed the same way. Only fetched in
176411
+ // preview, where the tab shows both of the profile owner's collections.
176412
+ const { data: mentorMemoriesData, isLoading: isLoadingMentorMemories } = useGetAllMentorMemoriesQuery({
175993
176413
  org,
175994
- userId: username,
175995
- params: { page: currentPage, page_size: MEMORIES_PAGE_SIZE },
175996
- });
175997
- const totalPages = (memoriesData === null || memoriesData === void 0 ? void 0 : memoriesData.count) ? Math.ceil(memoriesData.count / MEMORIES_PAGE_SIZE) : 0;
176414
+ userId: currentUsername,
176415
+ params: { ...memoriesParams, user_id: username },
176416
+ }, { skip: !isPreview || !currentUsername });
176417
+ const memories = useMemo(() => {
176418
+ var _a, _b;
176419
+ const globalMemories = (_a = globalMemoriesData === null || globalMemoriesData === void 0 ? void 0 : globalMemoriesData.results) !== null && _a !== void 0 ? _a : [];
176420
+ if (!isPreview)
176421
+ return globalMemories;
176422
+ // Both collections are newest-first on their own; interleave them so the
176423
+ // combined list still reads chronologically.
176424
+ return [...((_b = mentorMemoriesData === null || mentorMemoriesData === void 0 ? void 0 : mentorMemoriesData.results) !== null && _b !== void 0 ? _b : []), ...globalMemories].sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
176425
+ }, [isPreview, globalMemoriesData, mentorMemoriesData]);
176426
+ const isLoadingMemories = isLoadingGlobalMemories || (isPreview && isLoadingMentorMemories);
176427
+ // The two collections page independently at the same size, so a page shows
176428
+ // that page of each and the pager spans whichever collection is longer.
176429
+ const totalPages = useMemo(() => {
176430
+ const pageCount = (count) => (count ? Math.ceil(count / MEMORIES_PAGE_SIZE) : 0);
176431
+ const globalPages = pageCount(globalMemoriesData === null || globalMemoriesData === void 0 ? void 0 : globalMemoriesData.count);
176432
+ return isPreview ? Math.max(globalPages, pageCount(mentorMemoriesData === null || mentorMemoriesData === void 0 ? void 0 : mentorMemoriesData.count)) : globalPages;
176433
+ }, [isPreview, globalMemoriesData, mentorMemoriesData]);
175998
176434
  const [deleteMemory] = useDeleteGlobalMemoryMutation();
175999
176435
  const handleToggleSetting = async (key, checked) => {
176000
176436
  try {
@@ -176033,7 +176469,7 @@ function MemoryTab({ org, username }) {
176033
176469
  if (!isConfigError && memsearchStatus && !memsearchStatus.enable_memsearch) {
176034
176470
  return (jsx("div", { className: "max-w-2xl", children: jsx("div", { className: "rounded-lg border px-6 py-8 text-center", style: { borderColor: 'oklch(.922 0 0)' }, children: jsx("p", { className: "text-sm text-gray-500", children: t('memoryMemoryTab.memoryFeatureDisabled') }) }) }));
176035
176471
  }
176036
- return (jsxs("div", { className: "space-y-8", children: [jsxs("div", { className: "space-y-4", children: [jsx("h4", { className: "text-sm font-semibold text-gray-700 dark:text-gray-300 tracking-wide", children: t('memoryMemoryTab.sectionMemoryPersonalization') }), jsxs("div", { className: "flex items-center justify-between rounded-lg border px-6 py-5", style: { borderColor: 'oklch(.922 0 0)' }, children: [jsxs("div", { className: "flex-1 mr-4", children: [jsx("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: t('memoryMemoryTab.autoCaptureLabelTitle') }), jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: t('memoryMemoryTab.autoCaptureLabelDesc') })] }), jsx(Switch, { checked: (_a = settings === null || settings === void 0 ? void 0 : settings.auto_capture_enabled) !== null && _a !== void 0 ? _a : false, onCheckedChange: (checked) => handleToggleSetting('auto_capture_enabled', checked), disabled: isUpdatingSettings, "aria-label": t('memoryMemoryTab.autoCaptureAriaLabel', {
176472
+ return (jsxs("div", { className: "space-y-8", children: [!isPreview && (jsxs("div", { className: "space-y-4", children: [!isPreview && (jsx("h4", { className: "text-sm font-semibold text-gray-700 dark:text-gray-300 tracking-wide", children: t('memoryMemoryTab.sectionMemoryPersonalization') })), jsxs("div", { className: "flex items-center justify-between rounded-lg border px-6 py-5", style: { borderColor: 'oklch(.922 0 0)' }, children: [jsxs("div", { className: "flex-1 mr-4", children: [jsx("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: t('memoryMemoryTab.autoCaptureLabelTitle') }), jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: t('memoryMemoryTab.autoCaptureLabelDesc') })] }), jsx(Switch, { checked: (_a = settings === null || settings === void 0 ? void 0 : settings.auto_capture_enabled) !== null && _a !== void 0 ? _a : false, onCheckedChange: (checked) => handleToggleSetting('auto_capture_enabled', checked), disabled: isUpdatingSettings, "aria-label": t('memoryMemoryTab.autoCaptureAriaLabel', {
176037
176473
  state: (settings === null || settings === void 0 ? void 0 : settings.auto_capture_enabled)
176038
176474
  ? t('memoryMemoryTab.stateEnabled')
176039
176475
  : t('memoryMemoryTab.stateDisabled'),
@@ -176041,9 +176477,13 @@ function MemoryTab({ org, username }) {
176041
176477
  state: (settings === null || settings === void 0 ? void 0 : settings.use_memory_in_responses)
176042
176478
  ? t('memoryMemoryTab.stateEnabled')
176043
176479
  : t('memoryMemoryTab.stateDisabled'),
176044
- }), className: "cursor-pointer data-[state=checked]:bg-blue-500 flex-shrink-0" })] })] }), jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-center justify-between", children: [jsx("h4", { className: "text-sm font-semibold text-gray-700 dark:text-gray-300 tracking-wide", children: t('memoryMemoryTab.sectionMyMemories') }), jsxs(Button$1, { variant: "outline", size: "sm", onClick: () => setIsAddDialogOpen(true), className: "text-xs", children: [jsx(Plus, { className: "h-3 w-3 mr-1" }), t('memoryMemoryTab.addMemoryButton')] })] }), isLoadingMemories ? (jsx("div", { className: "space-y-3", children: [1, 2, 3].map((i) => (jsx(Skeleton, { className: "h-16 w-full rounded-lg" }, i))) })) : ((_c = memoriesData === null || memoriesData === void 0 ? void 0 : memoriesData.results) === null || _c === void 0 ? void 0 : _c.length) ? (jsx("div", { className: "space-y-3", children: memoriesData.results.map((memory) => (jsxs("div", { className: "group flex items-start gap-3 rounded-lg border px-4 py-3", style: { borderColor: 'oklch(.922 0 0)' }, children: [jsx("div", { className: "flex-shrink-0 mt-0.5", children: memory.is_auto_generated ? (jsx(Bot, { className: "h-4 w-4 text-blue-500", "aria-label": t('memoryMemoryTab.autoGeneratedMemoryAriaLabel') })) : (jsx(User, { className: "h-4 w-4 text-gray-400", "aria-label": t('memoryMemoryTab.manuallyAddedMemoryAriaLabel') })) }), jsxs("div", { className: "flex-1 min-w-0", children: [jsx("p", { className: "text-sm text-gray-800 dark:text-gray-200", children: memory.content }), jsxs("p", { className: "text-xs text-gray-400 mt-1", children: [new Date(memory.created_at).toLocaleDateString(), memory.is_auto_generated && (jsx("span", { className: "ml-2 inline-flex items-center rounded-full bg-blue-50 px-1.5 py-0.5 text-[10px] font-medium text-blue-600", children: t('memoryMemoryTab.autoBadge') }))] })] }), jsx("button", { type: "button", onClick: () => handleDeleteMemory(memory.id), disabled: deletingId === memory.id, className: "flex-shrink-0 opacity-0 group-hover:opacity-100 transition-opacity p-1 rounded hover:bg-gray-100 text-gray-400 hover:text-gray-600 focus:opacity-100 focus:outline-none focus:ring-2 focus:ring-gray-300", "aria-label": t('memoryMemoryTab.deleteMemoryAriaLabel', {
176480
+ }), className: "cursor-pointer data-[state=checked]:bg-blue-500 flex-shrink-0" })] })] })), jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-center justify-between", children: [jsx("h4", { className: "text-sm font-semibold text-gray-700 dark:text-gray-300 tracking-wide", children: isPreview
176481
+ ? t('memoryMemoryTab.sectionMyMemoriesPreview')
176482
+ : t('memoryMemoryTab.sectionMyMemories') }), !isPreview && (jsxs(Button$1, { variant: "outline", size: "sm", onClick: () => setIsAddDialogOpen(true), className: "text-xs", children: [jsx(Plus, { className: "h-3 w-3 mr-1" }), t('memoryMemoryTab.addMemoryButton')] }))] }), isLoadingMemories ? (jsx("div", { className: "space-y-3", children: [1, 2, 3].map((i) => (jsx(Skeleton, { className: "h-16 w-full rounded-lg" }, i))) })) : memories.length ? (jsx("div", { className: "space-y-3", children: memories.map((memory) => (jsxs("div", { className: "group flex items-start gap-3 rounded-lg border px-4 py-3", style: { borderColor: 'oklch(.922 0 0)' }, children: [jsx("div", { className: "flex-shrink-0 mt-0.5", children: memory.is_auto_generated ? (jsx(Bot, { className: "h-4 w-4 text-blue-500", "aria-label": t('memoryMemoryTab.autoGeneratedMemoryAriaLabel') })) : (jsx(User, { className: "h-4 w-4 text-gray-400", "aria-label": t('memoryMemoryTab.manuallyAddedMemoryAriaLabel') })) }), jsxs("div", { className: "flex-1 min-w-0", children: [jsx("p", { className: "text-sm text-gray-800 dark:text-gray-200", children: memory.content }), jsxs("p", { className: "text-xs text-gray-400 mt-1", children: [new Date(memory.created_at).toLocaleDateString(), memory.is_auto_generated && (jsx("span", { className: "ml-2 inline-flex items-center rounded-full bg-blue-50 px-1.5 py-0.5 text-[10px] font-medium text-blue-600", children: t('memoryMemoryTab.autoBadge') }))] })] }), !isPreview && (jsx("button", { type: "button", onClick: () => handleDeleteMemory(memory.id), disabled: deletingId === memory.id, className: "flex-shrink-0 opacity-0 group-hover:opacity-100 transition-opacity p-1 rounded hover:bg-gray-100 text-gray-400 hover:text-gray-600 focus:opacity-100 focus:outline-none focus:ring-2 focus:ring-gray-300", "aria-label": t('memoryMemoryTab.deleteMemoryAriaLabel', {
176045
176483
  excerpt: memory.content.substring(0, 30),
176046
- }), children: deletingId === memory.id ? (jsx("div", { className: "w-4 h-4 border-2 border-gray-500 border-t-transparent rounded-full animate-spin" })) : (jsx(Trash2, { className: "h-4 w-4" })) })] }, memory.id))) })) : (jsx("div", { className: "rounded-lg border px-6 py-8 text-center", style: { borderColor: 'oklch(.922 0 0)' }, children: jsx("p", { className: "text-sm text-gray-500", children: t('memoryMemoryTab.noMemoriesYet') }) })), totalPages > 1 && (jsx(IblPagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: setCurrentPage, disabled: isLoadingMemories }))] }), jsx(AddMemoryDialog, { open: isAddDialogOpen, onOpenChange: setIsAddDialogOpen, org: org, username: username })] }));
176484
+ }), children: deletingId === memory.id ? (jsx("div", { className: "w-4 h-4 border-2 border-gray-500 border-t-transparent rounded-full animate-spin" })) : (jsx(Trash2, { className: "h-4 w-4" })) }))] }, `${'mentor_id' in memory ? memory.mentor_id : 'global'}-${memory.id}`))) })) : (jsx("div", { className: "rounded-lg border px-6 py-8 text-center", style: { borderColor: 'oklch(.922 0 0)' }, children: jsx("p", { className: "text-sm text-gray-500", children: isPreview
176485
+ ? t('memoryMemoryTab.noMemoriesYetPreview')
176486
+ : t('memoryMemoryTab.noMemoriesYet') }) })), totalPages > 1 && (jsx(IblPagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: setCurrentPage, disabled: isLoadingMemories }))] }), jsx(AddMemoryDialog, { open: isAddDialogOpen, onOpenChange: setIsAddDialogOpen, org: org, username: username })] }));
176047
176487
  }
176048
176488
 
176049
176489
  var _a$1;
@@ -178184,7 +178624,9 @@ const PopoverContent = React.forwardRef(({ className, align = 'center', sideOffs
178184
178624
  });
178185
178625
  PopoverContent.displayName = Content2$2.displayName;
178186
178626
 
178627
+ const PAGE_SIZE$a = 10;
178187
178628
  function UsersTab({ tenant, onInviteClick, currentSpa, showGradebookTab = false, }) {
178629
+ var _a, _b;
178188
178630
  const t = useT();
178189
178631
  const dispatch = useDispatch();
178190
178632
  const [page, setPage] = useState(1);
@@ -178196,14 +178638,35 @@ function UsersTab({ tenant, onInviteClick, currentSpa, showGradebookTab = false,
178196
178638
  const [updatingUserIds, setUpdatingUserIds] = useState(new Set());
178197
178639
  const [openPolicyPopover, setOpenPolicyPopover] = useState(null);
178198
178640
  const [selectedUser, setSelectedUser] = useState(null);
178199
- const { data: users, refetch: refetchPlatformUsers, isLoading, } = usePlatformUsersQuery({
178641
+ const { currentTenant } = useCurrentTenant();
178642
+ const rbacPermissions = useSelector(selectRbacPermissions);
178643
+ // A pure watcher administers nothing: the platform users endpoint 403s for
178644
+ // them, so they get the RBAC-scoped list of users they may read instead —
178645
+ // name and email only, with no invite, role, policy or status controls.
178646
+ const isWatcher = isPureWatcher(rbacPermissions, currentTenant);
178647
+ const { data: users, refetch: refetchPlatformUsers, isLoading: isLoadingPlatformUsers, } = usePlatformUsersQuery({
178200
178648
  page,
178201
- page_size: 10,
178649
+ page_size: PAGE_SIZE$a,
178202
178650
  platform_key: tenant,
178203
178651
  platform_org: tenant,
178204
178652
  query: debouncedSearchQuery,
178205
178653
  return_policies: 'true',
178206
- });
178654
+ }, { skip: isWatcher });
178655
+ // The accessible endpoint scopes itself to the caller's platform from their
178656
+ // token, so it takes no tenant param. It also filters per field instead of
178657
+ // with one search param, hence the "@" routing the term to `email` and
178658
+ // anything else to `username`.
178659
+ const { data: accessibleUsers, isLoading: isLoadingAccessibleUsers } = useGetAccessibleUsersQuery({
178660
+ // TODO to use required_action: 'Ibl.Core/Users/read',
178661
+ required_action: 'Ibl.Mentor/Memory/list',
178662
+ page,
178663
+ page_size: PAGE_SIZE$a,
178664
+ sort: 'name',
178665
+ ...(debouncedSearchQuery.includes('@')
178666
+ ? { email: debouncedSearchQuery }
178667
+ : { username: debouncedSearchQuery }),
178668
+ }, { skip: !isWatcher });
178669
+ const isLoading = isWatcher ? isLoadingAccessibleUsers : isLoadingPlatformUsers;
178207
178670
  useEffect(() => {
178208
178671
  const handler = setTimeout(() => {
178209
178672
  if (searchQuery.length > 2) {
@@ -178220,7 +178683,10 @@ function UsersTab({ tenant, onInviteClick, currentSpa, showGradebookTab = false,
178220
178683
  }, [searchQuery]);
178221
178684
  const handlePageChange = async (newPage) => {
178222
178685
  setPage(newPage);
178223
- await refetchPlatformUsers();
178686
+ // The watcher list is served by the other (skipped) query — changing `page`
178687
+ // is enough to refetch it.
178688
+ if (!isWatcher)
178689
+ await refetchPlatformUsers();
178224
178690
  };
178225
178691
  const handleUserRoleChange = async (user) => {
178226
178692
  const userId = user === null || user === void 0 ? void 0 : user.user_id;
@@ -178338,8 +178804,6 @@ function UsersTab({ tenant, onInviteClick, currentSpa, showGradebookTab = false,
178338
178804
  });
178339
178805
  }
178340
178806
  };
178341
- // Calculate total pages
178342
- const totalPages = users ? Math.ceil(users.count / 10) : 0; // Assuming 10 users per page
178343
178807
  // Extract allowed policies and user data
178344
178808
  const isResultsWithPolicies = (users === null || users === void 0 ? void 0 : users.results) ? isPoliciesResponse(users.results) : false;
178345
178809
  const allowedPolicies = isResultsWithPolicies
@@ -178348,31 +178812,55 @@ function UsersTab({ tenant, onInviteClick, currentSpa, showGradebookTab = false,
178348
178812
  const userData = isResultsWithPolicies
178349
178813
  ? (users === null || users === void 0 ? void 0 : users.results).data
178350
178814
  : [];
178351
- return (jsxs("div", { className: "mt-4", children: [jsxs("div", { className: "flex justify-between items-center gap-1", children: [jsxs("div", { className: "relative mb-6 max-w-sm", children: [jsx(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400" }), jsx(Input, { type: "text", value: searchQuery, onChange: (event) => setQuerySearch(event.target.value), placeholder: t('tabsUserstab.searchPlaceholder'), className: "pl-10 pr-10 focus:ring-blue-500 focus:border-blue-500 h-[35px]", "aria-label": t('tabsUserstab.searchAriaLabel') }), searchQuery.length > 0 && (searchQuery !== debouncedSearchQuery || isLoading) && (jsx(LoaderCircle, { "data-testid": "search-spinner", className: "absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400 animate-spin", "aria-hidden": "true" }))] }), jsx("div", { className: "mb-6", children: jsx(Button$1, { className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] hover:opacity-90 text-white h-[35px]", onClick: onInviteClick, children: t('tabsUserstab.inviteButton') }) })] }), isLoading ? (jsx("div", { className: "flex justify-center items-center h-64", children: jsx(Spinner, {}) })) : (jsxs(Fragment$1, { children: [jsx("div", { className: "bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg overflow-x-auto", children: jsxs("table", { className: "w-full text-sm", children: [jsx("thead", { className: "bg-gray-50 dark:bg-gray-700 border-b border-gray-200 dark:border-gray-600", children: jsxs("tr", { children: [jsx("th", { className: "px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400 w-[16%]", children: t('tabsUserstab.columnName') }), jsx("th", { className: "px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400 w-[25%]", children: t('tabsUserstab.columnEmail') }), jsx("th", { className: "px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400 w-[16%]", children: t('tabsUserstab.columnRole') }), jsx("th", { className: "px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400 w-[25%]", children: t('tabsUserstab.columnPolicies') }), jsx("th", { className: "px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400 w-[18%]", children: t('tabsUserstab.columnStatus') })] }) }), jsxs("tbody", { className: "divide-y divide-gray-200 dark:divide-gray-600", children: [userData.map((user, index) => (jsxs("tr", { onClick: () => setSelectedUser(user), className: "cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors", children: [jsx("td", { "aria-label": 'user-management-name-column', className: "px-6 py-4 text-gray-900 dark:text-gray-100 text-sm whitespace-nowrap overflow-hidden text-ellipsis max-w-0", children: jsx("button", { type: "button", onClick: (e) => {
178352
- e.stopPropagation();
178353
- setSelectedUser(user);
178354
- }, className: "block max-w-full truncate text-left hover:text-blue-600 hover:underline focus:outline-none focus:text-blue-600", "aria-label": t('tabsUserstab.viewProfileAriaLabel', { name: user.name }), children: user.name }) }), jsx("td", { "aria-label": 'user-management-email-column', className: "px-6 py-4 text-gray-600 dark:text-gray-300 text-sm max-w-0", children: jsx("div", { className: "truncate", children: user.email }) }), jsx("td", { className: "px-6 py-4", onClick: (e) => e.stopPropagation(), children: jsxs(Select$1, { value: user.is_admin ? 'admin' : 'student', onValueChange: () => handleUserRoleChange(user), children: [jsx(SelectTrigger, { className: "w-full text-sm", children: jsx(SelectValue, {}) }), jsxs(SelectContent, { className: "text-sm", children: [jsx(SelectItem, { value: "admin", children: t('tabsUserstab.roleAdmin') }), jsx(SelectItem, { value: "student", children: t('tabsUserstab.roleUser') })] })] }) }), jsx("td", { className: "px-6 py-4", onClick: (e) => e.stopPropagation(), children: jsxs(Popover, { modal: true, open: openPolicyPopover === user.user_id, onOpenChange: (open) => setOpenPolicyPopover(open ? user.user_id : null), children: [jsx(PopoverTrigger, { asChild: true, children: jsxs(Button$1, { variant: "outline", role: "combobox", "aria-expanded": openPolicyPopover === user.user_id, "aria-label": t('tabsUserstab.selectPoliciesAriaLabel', {
178815
+ const rows = isWatcher
178816
+ ? ((_a = accessibleUsers === null || accessibleUsers === void 0 ? void 0 : accessibleUsers.results) !== null && _a !== void 0 ? _a : []).map((user) => ({
178817
+ key: user.id,
178818
+ name: user.name,
178819
+ email: user.email,
178820
+ username: user.username,
178821
+ isAdmin: false,
178822
+ }))
178823
+ : userData.map((user) => ({
178824
+ key: user.user_id,
178825
+ name: user.name,
178826
+ email: user.email,
178827
+ username: user.username,
178828
+ isAdmin: user.is_admin,
178829
+ managed: user,
178830
+ }));
178831
+ const totalCount = (_b = (isWatcher ? accessibleUsers === null || accessibleUsers === void 0 ? void 0 : accessibleUsers.count : users === null || users === void 0 ? void 0 : users.count)) !== null && _b !== void 0 ? _b : 0;
178832
+ const totalPages = Math.ceil(totalCount / PAGE_SIZE$a);
178833
+ return (jsxs("div", { className: "mt-4", children: [jsxs("div", { className: "flex justify-between items-center gap-1", children: [jsxs("div", { className: "relative mb-6 max-w-sm", children: [jsx(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400" }), jsx(Input, { type: "text", value: searchQuery, onChange: (event) => setQuerySearch(event.target.value), placeholder: t('tabsUserstab.searchPlaceholder'), className: "pl-10 pr-10 focus:ring-blue-500 focus:border-blue-500 h-[35px]", "aria-label": t('tabsUserstab.searchAriaLabel') }), searchQuery.length > 0 && (searchQuery !== debouncedSearchQuery || isLoading) && (jsx(LoaderCircle, { "data-testid": "search-spinner", className: "absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400 animate-spin", "aria-hidden": "true" }))] }), !isWatcher && (jsx("div", { className: "mb-6", children: jsx(Button$1, { className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] hover:opacity-90 text-white h-[35px]", onClick: onInviteClick, children: t('tabsUserstab.inviteButton') }) }))] }), isLoading ? (jsx("div", { className: "flex justify-center items-center h-64", children: jsx(Spinner, {}) })) : (jsxs(Fragment$1, { children: [jsx("div", { className: "bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg overflow-x-auto", children: jsxs("table", { className: "w-full text-sm", children: [jsx("thead", { className: "bg-gray-50 dark:bg-gray-700 border-b border-gray-200 dark:border-gray-600", children: jsxs("tr", { children: [jsx("th", { className: `px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400 ${isWatcher ? 'w-[40%]' : 'w-[16%]'}`, children: t('tabsUserstab.columnName') }), jsx("th", { className: `px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400 ${isWatcher ? 'w-[60%]' : 'w-[25%]'}`, children: t('tabsUserstab.columnEmail') }), !isWatcher && (jsxs(Fragment$1, { children: [jsx("th", { className: "px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400 w-[16%]", children: t('tabsUserstab.columnRole') }), jsx("th", { className: "px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400 w-[25%]", children: t('tabsUserstab.columnPolicies') }), jsx("th", { className: "px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400 w-[18%]", children: t('tabsUserstab.columnStatus') })] }))] }) }), jsxs("tbody", { className: "divide-y divide-gray-200 dark:divide-gray-600", children: [rows.map((row) => {
178834
+ // Only set on the admin path — see {@link UserRow}.
178835
+ const user = row.managed;
178836
+ return (jsxs("tr", { onClick: () => setSelectedUser(row), className: "cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors", children: [jsx("td", { "aria-label": 'user-management-name-column', className: "px-6 py-4 text-gray-900 dark:text-gray-100 text-sm whitespace-nowrap overflow-hidden text-ellipsis max-w-0", children: jsx("button", { type: "button", onClick: (e) => {
178837
+ e.stopPropagation();
178838
+ setSelectedUser(row);
178839
+ }, className: "block max-w-full truncate text-left hover:text-blue-600 hover:underline focus:outline-none focus:text-blue-600", "aria-label": t('tabsUserstab.viewProfileAriaLabel', { name: row.name }), children: row.name }) }), jsx("td", { "aria-label": 'user-management-email-column', className: "px-6 py-4 text-gray-600 dark:text-gray-300 text-sm max-w-0", children: jsx("div", { className: "truncate", children: row.email }) }), user && (jsxs(Fragment$1, { children: [jsx("td", { className: "px-6 py-4", onClick: (e) => e.stopPropagation(), children: jsxs(Select$1, { value: user.is_admin ? 'admin' : 'student', onValueChange: () => handleUserRoleChange(user), children: [jsx(SelectTrigger, { className: "w-full text-sm", children: jsx(SelectValue, {}) }), jsxs(SelectContent, { className: "text-sm", children: [jsx(SelectItem, { value: "admin", children: t('tabsUserstab.roleAdmin') }), jsx(SelectItem, { value: "student", children: t('tabsUserstab.roleUser') })] })] }) }), jsx("td", { className: "px-6 py-4", onClick: (e) => e.stopPropagation(), children: jsxs(Popover, { modal: true, open: openPolicyPopover === user.user_id, onOpenChange: (open) => setOpenPolicyPopover(open ? user.user_id : null), children: [jsx(PopoverTrigger, { asChild: true, children: jsxs(Button$1, { variant: "outline", role: "combobox", "aria-expanded": openPolicyPopover === user.user_id, "aria-label": t('tabsUserstab.selectPoliciesAriaLabel', {
178840
+ name: user.name,
178841
+ }), className: "w-full justify-between text-sm h-9 font-normal", disabled: updatingUserIds.has(user === null || user === void 0 ? void 0 : user.user_id), children: [jsx("span", { className: "truncate", children: user.is_admin
178842
+ ? t('tabsUserstab.allPoliciesSelected', {
178843
+ count: allowedPolicies.length,
178844
+ })
178845
+ : user.policies && user.policies.length > 0
178846
+ ? t('tabsUserstab.nPoliciesSelected', {
178847
+ count: user.policies.length,
178848
+ })
178849
+ : t('tabsUserstab.noPolicies') }), openPolicyPopover === user.user_id ? (jsx(ChevronUp, { className: "ml-2 h-4 w-4 shrink-0 opacity-50" })) : (jsx(ChevronDown, { className: "ml-2 h-4 w-4 shrink-0 opacity-50" }))] }) }), jsx(PopoverContent, { className: "w-64 p-3", align: "start", children: jsxs("div", { className: "space-y-2", children: [jsx("div", { className: "text-sm font-medium mb-2", children: t('tabsUserstab.selectPoliciesHeading') }), allowedPolicies.map((policy) => {
178850
+ var _a;
178851
+ return (jsxs("div", { className: "flex items-center space-x-2", children: [jsx(Checkbox, { id: `${user.user_id}-${policy}`, checked: user.is_admin || ((_a = user.policies) === null || _a === void 0 ? void 0 : _a.includes(policy)), onCheckedChange: (checked) => handlePolicyChange(user, policy, checked), disabled: user.is_admin || updatingUserIds.has(user === null || user === void 0 ? void 0 : user.user_id), "aria-label": t('tabsUserstab.togglePolicyAriaLabel', {
178852
+ policy,
178853
+ name: user.name,
178854
+ }) }), jsx("label", { htmlFor: `${user.user_id}-${policy}`, className: `text-sm font-normal ${user.is_admin ? 'cursor-default' : 'cursor-pointer'}`, children: policy })] }, policy));
178855
+ })] }) })] }) }), jsx("td", { className: "px-6 py-4", onClick: (e) => e.stopPropagation(), children: jsx(Switch, { checked: user.active, onCheckedChange: (checked) => handleUserStatusChange(user, checked), disabled: updatingUserIds.has(user === null || user === void 0 ? void 0 : user.user_id), "aria-label": t('tabsUserstab.toggleStatusAriaLabel', {
178355
178856
  name: user.name,
178356
- }), className: "w-full justify-between text-sm h-9 font-normal", disabled: updatingUserIds.has(user === null || user === void 0 ? void 0 : user.user_id), children: [jsx("span", { className: "truncate", children: user.is_admin
178357
- ? t('tabsUserstab.allPoliciesSelected', {
178358
- count: allowedPolicies.length,
178359
- })
178360
- : user.policies && user.policies.length > 0
178361
- ? t('tabsUserstab.nPoliciesSelected', {
178362
- count: user.policies.length,
178363
- })
178364
- : t('tabsUserstab.noPolicies') }), openPolicyPopover === user.user_id ? (jsx(ChevronUp, { className: "ml-2 h-4 w-4 shrink-0 opacity-50" })) : (jsx(ChevronDown, { className: "ml-2 h-4 w-4 shrink-0 opacity-50" }))] }) }), jsx(PopoverContent, { className: "w-64 p-3", align: "start", children: jsxs("div", { className: "space-y-2", children: [jsx("div", { className: "text-sm font-medium mb-2", children: t('tabsUserstab.selectPoliciesHeading') }), allowedPolicies.map((policy) => {
178365
- var _a;
178366
- return (jsxs("div", { className: "flex items-center space-x-2", children: [jsx(Checkbox, { id: `${user.user_id}-${policy}`, checked: user.is_admin || ((_a = user.policies) === null || _a === void 0 ? void 0 : _a.includes(policy)), onCheckedChange: (checked) => handlePolicyChange(user, policy, checked), disabled: user.is_admin || updatingUserIds.has(user === null || user === void 0 ? void 0 : user.user_id), "aria-label": t('tabsUserstab.togglePolicyAriaLabel', {
178367
- policy,
178368
- name: user.name,
178369
- }) }), jsx("label", { htmlFor: `${user.user_id}-${policy}`, className: `text-sm font-normal ${user.is_admin ? 'cursor-default' : 'cursor-pointer'}`, children: policy })] }, policy));
178370
- })] }) })] }) }), jsx("td", { className: "px-6 py-4", onClick: (e) => e.stopPropagation(), children: jsx(Switch, { checked: user.active, onCheckedChange: (checked) => handleUserStatusChange(user, checked), disabled: updatingUserIds.has(user === null || user === void 0 ? void 0 : user.user_id), "aria-label": t('tabsUserstab.toggleStatusAriaLabel', { name: user.name }), className: "cursor-pointer data-[state=checked]:bg-blue-500 disabled:cursor-not-allowed disabled:opacity-50" }) })] }, index))), (!userData || userData.length === 0) && (jsx("tr", { children: jsx("td", { colSpan: 5, className: "px-6 py-8 text-center text-gray-500 dark:text-gray-400", children: t('tabsUserstab.noUsersFound') }) }))] })] }) }), jsx(AdvancedPagination, { variant: "footer", className: "-mx-6 -mb-6 mt-4 rounded-b-lg", totalPages: totalPages, currentPage: page, onPageChange: (_page) => handlePageChange(_page), totalCount: users === null || users === void 0 ? void 0 : users.count, itemsPerPage: 10, itemLabel: "users" })] })), selectedUser && (jsx(Dialog, { open: !!selectedUser, onOpenChange: (open) => {
178857
+ }), className: "cursor-pointer data-[state=checked]:bg-blue-500 disabled:cursor-not-allowed disabled:opacity-50" }) })] }))] }, row.key));
178858
+ }), rows.length === 0 && (jsx("tr", { children: jsx("td", { colSpan: isWatcher ? 2 : 5, className: "px-6 py-8 text-center text-gray-500 dark:text-gray-400", children: t('tabsUserstab.noUsersFound') }) }))] })] }) }), jsx(AdvancedPagination, { variant: "footer", className: "-mx-6 -mb-6 mt-4 rounded-b-lg", totalPages: totalPages, currentPage: page, onPageChange: (_page) => handlePageChange(_page), totalCount: totalCount, itemsPerPage: PAGE_SIZE$a, itemLabel: "users" })] })), selectedUser && (jsx(Dialog, { open: !!selectedUser, onOpenChange: (open) => {
178371
178859
  if (!open)
178372
178860
  setSelectedUser(null);
178373
- }, children: jsxs(DialogContent, { autoFocus: false, 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('tabsUserstab.profileDialogTitle') }), jsx(DialogDescription, { className: "sr-only", children: t('tabsUserstab.profileDialogDescription') }), jsx(Profile, { tenant: tenant, tenants: [], username: selectedUser.username, isAdmin: selectedUser.is_admin, currentSPA: currentSpa, onClose: () => setSelectedUser(null), customization: {
178861
+ }, children: jsxs(DialogContent, { autoFocus: false, 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('tabsUserstab.profileDialogTitle') }), jsx(DialogDescription, { className: "sr-only", children: t('tabsUserstab.profileDialogDescription') }), jsx(Profile, { tenant: tenant, tenants: [], username: selectedUser.username, isAdmin: selectedUser.isAdmin, currentSPA: currentSpa, onClose: () => setSelectedUser(null), customization: {
178374
178862
  showGradebookTab: showGradebookTab,
178375
- } })] }) }))] }));
178863
+ }, enableMemoryTab: true })] }) }))] }));
178376
178864
  }
178377
178865
 
178378
178866
  // Permission options for teams access
@@ -179587,7 +180075,7 @@ function PoliciesTab({ tenant }) {
179587
180075
  : t('tabsPoliciestab.createPolicy')] }) }) }), editing && (policyDetails === null || policyDetails === void 0 ? void 0 : policyDetails.is_internal) && (jsx(TooltipContent, { children: jsx("p", { children: t('tabsPoliciestab.cannotEditInternal') }) }))] }) })] })] }) }), jsx(ResourcePickerModal, { open: isResourcePickerOpen, onOpenChange: setIsResourcePickerOpen, tenant: tenant, onSelect: addPickedResource })] }));
179588
180076
  }
179589
180077
 
179590
- const PAGE_SIZE$8 = 10;
180078
+ const PAGE_SIZE$9 = 10;
179591
180079
  function normalizeEvents(events) {
179592
180080
  if (!events)
179593
180081
  return [];
@@ -179636,8 +180124,8 @@ function AlertsTab({ tenant }) {
179636
180124
  const { data: watchedGroupsData, isLoading, isError, refetch, } = useGetWatchedGroupsQuery({
179637
180125
  params: {
179638
180126
  platform_key: tenant,
179639
- limit: PAGE_SIZE$8,
179640
- offset: (page - 1) * PAGE_SIZE$8,
180127
+ limit: PAGE_SIZE$9,
180128
+ offset: (page - 1) * PAGE_SIZE$9,
179641
180129
  },
179642
180130
  });
179643
180131
  const groups = useMemo(() => {
@@ -179650,7 +180138,7 @@ function AlertsTab({ tenant }) {
179650
180138
  return results;
179651
180139
  }, [watchedGroupsData, debouncedSearchQuery]);
179652
180140
  const totalPages = watchedGroupsData
179653
- ? Math.max(1, Math.ceil(watchedGroupsData.count / PAGE_SIZE$8))
180141
+ ? Math.max(1, Math.ceil(watchedGroupsData.count / PAGE_SIZE$9))
179654
180142
  : 0;
179655
180143
  const handlePageChange = async (newPage) => {
179656
180144
  setPage(newPage);
@@ -179959,7 +180447,7 @@ function AlertsTab({ tenant }) {
179959
180447
  setDeletingGroupId(null);
179960
180448
  }
179961
180449
  }
179962
- return (jsxs("div", { className: "mt-4", children: [jsxs("div", { className: "flex mb-6 gap-2 flex-row items-center justify-between", children: [jsxs("div", { className: "relative max-w-sm w-full sm:w-auto", 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('tabsAlertstab.searchAlertsPlaceholder'), "aria-label": t('tabsAlertstab.searchAlertsAriaLabel'), className: "pl-10 pr-10 focus:ring-blue-500 focus:border-blue-500 h-[35px]" }), isLoading && (jsx(LoaderCircle, { "data-testid": "search-spinner", className: "absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400 animate-spin", "aria-hidden": "true" }))] }), jsx("div", { className: "flex gap-2", children: jsx(Button$1, { onClick: openNew, "aria-label": t('tabsAlertstab.newAlertAriaLabel'), className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] hover:opacity-90 text-white h-[35px]", children: t('tabsAlertstab.newAlert') }) })] }), isLoading && (jsx("div", { className: "flex justify-center items-center h-64", children: jsx("div", { className: "py-8 text-center text-sm text-gray-500 dark:text-gray-400", children: t('tabsAlertstab.loading') }) })), isError && (jsx("div", { className: "py-8 text-center text-sm text-red-500", children: t('tabsAlertstab.failedToLoadAlerts') })), !isLoading && !isError && (jsx("div", { className: "bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg overflow-x-auto", children: jsxs("table", { className: "w-full text-sm", children: [jsx("thead", { className: "bg-gray-50 dark:bg-gray-700 border-b border-gray-200 dark:border-gray-600", children: jsxs("tr", { children: [jsx("th", { className: "px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400", children: t('tabsAlertstab.columnName') }), jsx("th", { className: "px-6 py-3 text-right font-medium text-gray-500 dark:text-gray-400 w-[120px]" })] }) }), jsxs("tbody", { className: "divide-y divide-gray-200 dark:divide-gray-600", children: [groups.map((g) => (jsxs("tr", { children: [jsx("td", { className: "px-6 py-4 text-gray-900 dark:text-gray-100 text-sm", children: g.name }), jsx("td", { className: "px-6 py-4", children: jsxs("div", { className: "flex gap-2 justify-end", children: [jsx(Button$1, { variant: "ghost", size: "sm", onClick: () => openEdit(g), disabled: deletingGroupId === g.id, "aria-label": t('tabsAlertstab.editAlertAriaLabel', { name: g.name }), children: jsx(Pencil, { className: "h-4 w-4", "aria-hidden": "true" }) }), jsx(Button$1, { variant: "ghost", size: "sm", onClick: () => onDelete(g), className: "text-red-600 hover:text-red-700", disabled: deletingGroupId === g.id, "aria-label": t('tabsAlertstab.deleteAlertAriaLabel', { name: g.name }), children: deletingGroupId === g.id ? (jsx(LoaderCircle, { className: "h-4 w-4 animate-spin", "aria-hidden": "true" })) : (jsx(Trash2, { className: "h-4 w-4", "aria-hidden": "true" })) })] }) })] }, g.id))), groups.length === 0 && (jsx("tr", { children: jsx("td", { colSpan: 2, className: "px-6 py-8 text-center text-gray-500 dark:text-gray-400", children: t('tabsAlertstab.noAlertsFound') }) }))] })] }) })), !isLoading && !isError && (jsx(AdvancedPagination, { variant: "footer", className: "-mx-6 -mb-6 mt-4 rounded-b-lg", totalPages: totalPages, currentPage: page, onPageChange: (newPage) => handlePageChange(newPage), totalCount: watchedGroupsData === null || watchedGroupsData === void 0 ? void 0 : watchedGroupsData.count, itemsPerPage: PAGE_SIZE$8, itemLabel: "alerts" })), jsx(Dialog, { open: isOpen, onOpenChange: setIsOpen, children: jsxs(DialogContent, { "aria-describedby": undefined, className: "max-w-2xl max-h-[90vh] overflow-y-auto", children: [jsx(DialogHeader, { children: jsx(DialogTitle, { children: editingGroup ? t('tabsAlertstab.editAlert') : t('tabsAlertstab.newAlert') }) }), jsxs("div", { className: "space-y-5", children: [jsxs("div", { className: "grid gap-1.5", children: [jsx(Label$1, { htmlFor: "wg-name", children: t('tabsAlertstab.nameLabel') }), jsx(Input, { id: "wg-name", value: name, onChange: (e) => setName(e.target.value), placeholder: t('tabsAlertstab.enterAlertNamePlaceholder'), required: true })] }), jsxs("div", { className: "grid gap-1.5", children: [jsx(Label$1, { children: t('tabsAlertstab.watchedUsersLabel') }), jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: t('tabsAlertstab.watchedUsersDescription') }), watchedUsers.length > 0 ? (jsx("div", { className: "space-y-2 max-h-48 overflow-y-auto border border-gray-200 dark:border-gray-700 rounded-md p-2", children: watchedUsers.map((user) => (jsxs("div", { className: "flex items-center justify-between bg-gray-50 dark:bg-gray-800 p-2 rounded-md", children: [jsxs("div", { className: "flex flex-col", children: [jsx("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: user.name || user.username }), user.email && (jsx("span", { className: "text-xs text-gray-500 dark:text-gray-400", children: user.email }))] }), jsx(Button$1, { type: "button", variant: "ghost", size: "sm", onClick: () => removeWatchedUserDraft(user.id), className: "h-8 w-8 p-0 text-red-600 hover:text-red-700 hover:bg-red-50 dark:hover:bg-red-900/20", "aria-label": t('tabsAlertstab.removeWatchedUserAriaLabel', {
180450
+ return (jsxs("div", { className: "mt-4", children: [jsxs("div", { className: "flex mb-6 gap-2 flex-row items-center justify-between", children: [jsxs("div", { className: "relative max-w-sm w-full sm:w-auto", 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('tabsAlertstab.searchAlertsPlaceholder'), "aria-label": t('tabsAlertstab.searchAlertsAriaLabel'), className: "pl-10 pr-10 focus:ring-blue-500 focus:border-blue-500 h-[35px]" }), isLoading && (jsx(LoaderCircle, { "data-testid": "search-spinner", className: "absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400 animate-spin", "aria-hidden": "true" }))] }), jsx("div", { className: "flex gap-2", children: jsx(Button$1, { onClick: openNew, "aria-label": t('tabsAlertstab.newAlertAriaLabel'), className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] hover:opacity-90 text-white h-[35px]", children: t('tabsAlertstab.newAlert') }) })] }), isLoading && (jsx("div", { className: "flex justify-center items-center h-64", children: jsx("div", { className: "py-8 text-center text-sm text-gray-500 dark:text-gray-400", children: t('tabsAlertstab.loading') }) })), isError && (jsx("div", { className: "py-8 text-center text-sm text-red-500", children: t('tabsAlertstab.failedToLoadAlerts') })), !isLoading && !isError && (jsx("div", { className: "bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg overflow-x-auto", children: jsxs("table", { className: "w-full text-sm", children: [jsx("thead", { className: "bg-gray-50 dark:bg-gray-700 border-b border-gray-200 dark:border-gray-600", children: jsxs("tr", { children: [jsx("th", { className: "px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400", children: t('tabsAlertstab.columnName') }), jsx("th", { className: "px-6 py-3 text-right font-medium text-gray-500 dark:text-gray-400 w-[120px]" })] }) }), jsxs("tbody", { className: "divide-y divide-gray-200 dark:divide-gray-600", children: [groups.map((g) => (jsxs("tr", { children: [jsx("td", { className: "px-6 py-4 text-gray-900 dark:text-gray-100 text-sm", children: g.name }), jsx("td", { className: "px-6 py-4", children: jsxs("div", { className: "flex gap-2 justify-end", children: [jsx(Button$1, { variant: "ghost", size: "sm", onClick: () => openEdit(g), disabled: deletingGroupId === g.id, "aria-label": t('tabsAlertstab.editAlertAriaLabel', { name: g.name }), children: jsx(Pencil, { className: "h-4 w-4", "aria-hidden": "true" }) }), jsx(Button$1, { variant: "ghost", size: "sm", onClick: () => onDelete(g), className: "text-red-600 hover:text-red-700", disabled: deletingGroupId === g.id, "aria-label": t('tabsAlertstab.deleteAlertAriaLabel', { name: g.name }), children: deletingGroupId === g.id ? (jsx(LoaderCircle, { className: "h-4 w-4 animate-spin", "aria-hidden": "true" })) : (jsx(Trash2, { className: "h-4 w-4", "aria-hidden": "true" })) })] }) })] }, g.id))), groups.length === 0 && (jsx("tr", { children: jsx("td", { colSpan: 2, className: "px-6 py-8 text-center text-gray-500 dark:text-gray-400", children: t('tabsAlertstab.noAlertsFound') }) }))] })] }) })), !isLoading && !isError && (jsx(AdvancedPagination, { variant: "footer", className: "-mx-6 -mb-6 mt-4 rounded-b-lg", totalPages: totalPages, currentPage: page, onPageChange: (newPage) => handlePageChange(newPage), totalCount: watchedGroupsData === null || watchedGroupsData === void 0 ? void 0 : watchedGroupsData.count, itemsPerPage: PAGE_SIZE$9, itemLabel: "alerts" })), jsx(Dialog, { open: isOpen, onOpenChange: setIsOpen, children: jsxs(DialogContent, { "aria-describedby": undefined, className: "max-w-2xl max-h-[90vh] overflow-y-auto", children: [jsx(DialogHeader, { children: jsx(DialogTitle, { children: editingGroup ? t('tabsAlertstab.editAlert') : t('tabsAlertstab.newAlert') }) }), jsxs("div", { className: "space-y-5", children: [jsxs("div", { className: "grid gap-1.5", children: [jsx(Label$1, { htmlFor: "wg-name", children: t('tabsAlertstab.nameLabel') }), jsx(Input, { id: "wg-name", value: name, onChange: (e) => setName(e.target.value), placeholder: t('tabsAlertstab.enterAlertNamePlaceholder'), required: true })] }), jsxs("div", { className: "grid gap-1.5", children: [jsx(Label$1, { children: t('tabsAlertstab.watchedUsersLabel') }), jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: t('tabsAlertstab.watchedUsersDescription') }), watchedUsers.length > 0 ? (jsx("div", { className: "space-y-2 max-h-48 overflow-y-auto border border-gray-200 dark:border-gray-700 rounded-md p-2", children: watchedUsers.map((user) => (jsxs("div", { className: "flex items-center justify-between bg-gray-50 dark:bg-gray-800 p-2 rounded-md", children: [jsxs("div", { className: "flex flex-col", children: [jsx("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: user.name || user.username }), user.email && (jsx("span", { className: "text-xs text-gray-500 dark:text-gray-400", children: user.email }))] }), jsx(Button$1, { type: "button", variant: "ghost", size: "sm", onClick: () => removeWatchedUserDraft(user.id), className: "h-8 w-8 p-0 text-red-600 hover:text-red-700 hover:bg-red-50 dark:hover:bg-red-900/20", "aria-label": t('tabsAlertstab.removeWatchedUserAriaLabel', {
179963
180451
  username: user.username,
179964
180452
  }), children: jsx(X$2, { className: "h-4 w-4" }) })] }, user.id))) })) : (jsx("div", { className: "text-sm text-gray-500 dark:text-gray-400 p-4 text-center border border-dashed border-gray-300 dark:border-gray-600 rounded-md", children: t('tabsAlertstab.noWatchedUsersAdded') })), jsxs("div", { className: "relative", children: [jsx(Input, { value: watchedUserSearch, onChange: (e) => {
179965
180453
  setWatchedUserSearch(e.target.value);
@@ -181279,7 +181767,7 @@ function BlocksView({ platformKey, onBack, }) {
181279
181767
  }
181280
181768
 
181281
181769
  const FILTER_ORDER = ['all', ...APPLICATION_STATUSES];
181282
- const PAGE_SIZE$7 = 20;
181770
+ const PAGE_SIZE$8 = 20;
181283
181771
  const SEARCH_DEBOUNCE_MS = 300;
181284
181772
  /**
181285
181773
  * The public application URL on the auth SPA for one form of a platform
@@ -181412,7 +181900,7 @@ function SubmissionsPipeline({ platformKey, form, onBack, onOpen, }) {
181412
181900
  ...(sort !== '-created' ? { sort } : {}),
181413
181901
  ...candidateParams,
181414
181902
  page,
181415
- page_size: PAGE_SIZE$7,
181903
+ page_size: PAGE_SIZE$8,
181416
181904
  }, {
181417
181905
  refetchOnMountOrArgChange: true,
181418
181906
  });
@@ -181421,7 +181909,7 @@ function SubmissionsPipeline({ platformKey, form, onBack, onOpen, }) {
181421
181909
  : [];
181422
181910
  const rows = form ? allRows.filter((row) => row.form_id === form.id) : allRows;
181423
181911
  const count = typeof (pipeline === null || pipeline === void 0 ? void 0 : pipeline.count) === 'number' ? pipeline.count : allRows.length;
181424
- const totalPages = Math.max(1, Math.ceil(count / PAGE_SIZE$7));
181912
+ const totalPages = Math.max(1, Math.ceil(count / PAGE_SIZE$8));
181425
181913
  const counts = statsCounts(stats);
181426
181914
  const countFor = (key) => {
181427
181915
  if (!stats)
@@ -181459,7 +181947,7 @@ function SubmissionsPipeline({ platformKey, form, onBack, onOpen, }) {
181459
181947
  }) }) }), jsx("td", { className: "whitespace-nowrap px-6 py-4 text-sm text-gray-600 dark:text-gray-300", children: formatDate(row) }), jsx("td", { className: "px-6 py-4", children: jsx(Badge, { variant: "outline", className: approvalTone(row.status), children: STATUS_LABEL_KEYS[row.status]
181460
181948
  ? t(STATUS_LABEL_KEYS[row.status])
181461
181949
  : row.status }) })] }, row.id));
181462
- })) })] }) }), jsx(AdvancedPagination, { variant: "footer", className: "-mx-6 -mb-6 mt-4 rounded-b-lg", totalPages: totalPages, currentPage: page, onPageChange: (newPage) => setPage(newPage), totalCount: count, itemsPerPage: PAGE_SIZE$7, itemLabel: "applications" })] }))] }));
181950
+ })) })] }) }), jsx(AdvancedPagination, { variant: "footer", className: "-mx-6 -mb-6 mt-4 rounded-b-lg", totalPages: totalPages, currentPage: page, onPageChange: (newPage) => setPage(newPage), totalCount: count, itemsPerPage: PAGE_SIZE$8, itemLabel: "applications" })] }))] }));
181463
181951
  }
181464
181952
 
181465
181953
  const TabDescription = ({ icon, title, children }) => {
@@ -186451,6 +186939,64 @@ function CreateApiModal$1({ isOpen, onClose, tenantKey, username }) {
186451
186939
  : t('apiTabLabels.createModal.submit') })) }) })] })] }) }), apiKey && jsx(ApiKeyModal$1, { isOpen: true, onClose: () => setApiKey(null), apiKey: apiKey })] }));
186452
186940
  }
186453
186941
 
186942
+ /**
186943
+ * Returns the canonical logo path and display name for an LLM provider.
186944
+ * `logo` is a static-root path (e.g. `/llm-amazon-provider.png`); the package
186945
+ * ships the canonical assets under `@iblai/web-containers/public/` — copy them
186946
+ * into your app's static-root (`public/` for Next.js) so the paths resolve.
186947
+ */
186948
+ function getLLMProviderDetails(llmProvider, llmName) {
186949
+ switch (llmProvider) {
186950
+ case 'groq':
186951
+ return { logo: '/llm-groq-provider.png', name: 'Groq' };
186952
+ case 'azure_openai':
186953
+ return { logo: '/llm-microsoft-provider.png', name: 'Microsoft' };
186954
+ case 'openai':
186955
+ if (llmName)
186956
+ return { logo: '/llm-openai-provider.jpg', name: 'OpenAI' };
186957
+ return { logo: '/llm-openai-provider-2.svg', name: 'OpenAI' };
186958
+ case 'mistral':
186959
+ return { logo: '/llm-mistral-provider.jpeg', name: 'Mistral' };
186960
+ case 'google':
186961
+ if (llmName)
186962
+ return { logo: '/llm-gemini-provider.png', name: 'Google' };
186963
+ return { logo: '/llm-google-provider.svg', name: 'Google' };
186964
+ case 'llama':
186965
+ return { logo: '/llm-llama-provider.jpeg', name: 'Meta' };
186966
+ case 'perplexity':
186967
+ return { logo: '/llm-perplexity-provider.webp', name: 'Perplexity' };
186968
+ case 'deepseek':
186969
+ return { logo: '/llm-deepseek-provider.png', name: 'DeepSeek' };
186970
+ case 'xai':
186971
+ return { logo: '/llm-xai-provider.jpg', name: 'xAI' };
186972
+ case 'IBLChatAnthropic':
186973
+ case 'anthropic':
186974
+ return { logo: '/llm-claude-provider.png', name: 'Anthropic' };
186975
+ case 'IBLChatNvidia':
186976
+ case 'nvidia':
186977
+ return { logo: '/llm-nvidia-provider.webp', name: 'NVIDIA' };
186978
+ case 'bedrock':
186979
+ case 'amazon-bedrock':
186980
+ case 'amazon_bedrock':
186981
+ case 'IBLChatBedrock':
186982
+ return { logo: '/llm-amazon-provider.png', name: 'Amazon' };
186983
+ // ibl.ai's own hosted provider. The backend key is `iblai` (NameEnum.IBLAI);
186984
+ // the other cases cover the `IBLChat<Vendor>` house spelling and the
186985
+ // dotted/dashed/underscored brand forms seen in labels and older payloads.
186986
+ case 'iblai':
186987
+ case 'IBLAI':
186988
+ case 'ibl.ai':
186989
+ case 'ibl_ai':
186990
+ case 'ibl-ai':
186991
+ case 'ibl':
186992
+ case 'IBLChatIBL':
186993
+ case 'IBLChatIBLAI':
186994
+ return { logo: '/llm-iblai-provider.png', name: 'ibl.ai' };
186995
+ default:
186996
+ return { logo: '/llm-generic-provider.png', name: llmProvider };
186997
+ }
186998
+ }
186999
+
186454
187000
  // Dynamic schema based on selected provider
186455
187001
  const createDynamicSchema$1 = (schemaFields) => {
186456
187002
  const schemaObj = {
@@ -186679,10 +187225,14 @@ function CreateLLMModal({ isOpen, onClose, tenantKey, onSuccess }) {
186679
187225
  }
186680
187226
  }, children: [jsx(SelectTrigger, { children: jsx(SelectValue, { placeholder: t('llmsTabCreateLlmModal.selectProviderPlaceholder') }) }), jsx(SelectContent, { children: availableProviders.map((provider) => {
186681
187227
  var _a, _b;
186682
- const providerLogo = (_a = provider === null || provider === void 0 ? void 0 : provider.service_info) === null || _a === void 0 ? void 0 : _a.logo;
187228
+ // Prefer the logo the API ships; fall back to the
187229
+ // bundled provider artwork so known providers (ibl.ai
187230
+ // included) never degrade to a generic key emoji.
187231
+ const providerLogo = ((_a = provider === null || provider === void 0 ? void 0 : provider.service_info) === null || _a === void 0 ? void 0 : _a.logo) ||
187232
+ getLLMProviderDetails(provider.name).logo;
186683
187233
  const providerName = ((_b = provider === null || provider === void 0 ? void 0 : provider.service_info) === null || _b === void 0 ? void 0 : _b.display_name) ||
186684
187234
  provider.name;
186685
- return (jsx(SelectItem, { value: provider.name, children: jsxs("div", { className: "flex items-center gap-2", children: [providerLogo ? (jsx("img", { src: providerLogo, alt: provider.name, className: "w-4 h-4 rounded-sm object-contain" })) : (jsx("span", { children: "\uD83D\uDD11" })), providerName || provider.name] }) }, provider.name));
187235
+ return (jsx(SelectItem, { value: provider.name, children: jsxs("div", { className: "flex items-center gap-2", children: [jsx("img", { src: providerLogo, alt: provider.name, className: "w-4 h-4 rounded-sm object-contain" }), providerName || provider.name] }) }, provider.name));
186686
187236
  }) })] }), shouldShowError && (jsx("p", { className: "text-red-500 text-xs", children: (_b = field.state.meta.errors[0]) === null || _b === void 0 ? void 0 : _b.message }))] }));
186687
187237
  } })), isCredentialsSchemaLoading && (jsxs("div", { className: "flex items-center justify-center py-4", children: [jsx(LoaderCircle, { className: "h-4 w-4 animate-spin mr-2" }), jsx("span", { className: "text-sm text-gray-600", children: t('llmsTabCreateLlmModal.loadingProviderConfigurations') })] })), showProviderForm && !isCredentialsSchemaLoading && !isAzureOpenAI && (jsx(Fragment$1, { children: schemaFields.map((fieldName) => (jsx(form.Field, { name: fieldName, children: (field) => {
186688
187238
  var _a, _b;
@@ -186771,17 +187321,22 @@ function IntegrationLLMsTab({ tenantKey }) {
186771
187321
  return (jsxs("div", { className: "space-y-4", children: [jsx(InfoBanner, { icon: jsx(Bot, { className: "w-5 h-5" }), children: t('llmsTabIntegrationLlmsTab.infoBannerText') }), jsx("div", { className: "bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg overflow-x-auto", children: jsxs("table", { className: "w-full text-sm min-w-[600px]", children: [jsx("thead", { className: "bg-gray-50 dark:bg-gray-700 border-b border-gray-200 dark:border-gray-600", children: jsxs("tr", { children: [jsx("th", { className: "px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400", children: t('llmsTabIntegrationLlmsTab.colName') }), jsx("th", { className: "px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400", children: t('llmsTabIntegrationLlmsTab.colKey') }), jsx("th", { className: "px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400 w-[50px]" })] }) }), jsx("tbody", { className: "divide-y divide-gray-200 dark:divide-gray-600", children: isMaskedCredentialsLoading ? (jsx("tr", { children: jsx("td", { colSpan: 3, className: "px-6 py-8 text-center text-sm text-gray-500", children: t('llmsTabIntegrationLlmsTab.loading') }) })) : maskedCredentials.length === 0 ? (jsx("tr", { children: jsx("td", { colSpan: 3, className: "px-6 py-8 text-center text-sm text-gray-500", children: t('llmsTabIntegrationLlmsTab.noLlmKeysFound') }) })) : (maskedCredentials.map((cred, idx) => {
186772
187322
  var _a, _b, _c;
186773
187323
  const provider = ((_a = cred.service_info) === null || _a === void 0 ? void 0 : _a.display_name) || cred.name;
186774
- // Use logo from service_info only
187324
+ // Prefer the logo the API ships; fall back to the bundled
187325
+ // provider artwork so known providers (ibl.ai included) never
187326
+ // degrade to a generic key emoji.
186775
187327
  let logoUrl;
186776
187328
  let logoAlt;
186777
187329
  if ((_b = cred.service_info) === null || _b === void 0 ? void 0 : _b.logo) {
186778
187330
  logoUrl = cred.service_info.logo;
186779
187331
  logoAlt = cred.service_info.display_name || cred.service_info.name;
186780
187332
  }
187333
+ else {
187334
+ logoUrl = getLLMProviderDetails(cred.name).logo;
187335
+ }
186781
187336
  const isAzure = isAzureOpenAI(cred);
186782
187337
  const azureModels = isAzure ? getAzureModels(cred) : [];
186783
187338
  const isExpanded = expandedRows.has(cred.name);
186784
- return (jsxs(React__default.Fragment, { children: [jsxs("tr", { children: [jsx("td", { className: "px-6 py-4", children: jsxs("div", { className: "flex items-center gap-2", children: [logoUrl ? (jsx("img", { src: logoUrl, alt: logoAlt || provider || cred.name, className: "w-6 h-6 rounded-sm object-contain" })) : (jsx("span", { className: "text-lg", children: '🔑' })), jsx("span", { className: "font-normal text-gray-900 dark:text-gray-100", children: provider || cred.name }), isAzure && azureModels.length > 0 && (jsx("button", { type: "button", onClick: () => toggleRowExpansion(cred.name), className: "p-0.5 hover:bg-gray-100 rounded", children: isExpanded ? (jsx(ChevronUp, { className: "h-4 w-4 text-gray-500" })) : (jsx(ChevronDown, { className: "h-4 w-4 text-gray-500" })) }))] }) }), jsx("td", { className: "px-6 py-4 font-mono text-sm text-gray-900 dark:text-gray-100", children: isAzure ? (jsx("span", { className: "text-xs text-gray-500", children: t('llmsTabIntegrationLlmsTab.azureModelCount', {
187339
+ return (jsxs(React__default.Fragment, { children: [jsxs("tr", { children: [jsx("td", { className: "px-6 py-4", children: jsxs("div", { className: "flex items-center gap-2", children: [jsx("img", { src: logoUrl, alt: logoAlt || provider || cred.name, className: "w-6 h-6 rounded-sm object-contain" }), jsx("span", { className: "font-normal text-gray-900 dark:text-gray-100", children: provider || cred.name }), isAzure && azureModels.length > 0 && (jsx("button", { type: "button", onClick: () => toggleRowExpansion(cred.name), className: "p-0.5 hover:bg-gray-100 rounded", children: isExpanded ? (jsx(ChevronUp, { className: "h-4 w-4 text-gray-500" })) : (jsx(ChevronDown, { className: "h-4 w-4 text-gray-500" })) }))] }) }), jsx("td", { className: "px-6 py-4 font-mono text-sm text-gray-900 dark:text-gray-100", children: isAzure ? (jsx("span", { className: "text-xs text-gray-500", children: t('llmsTabIntegrationLlmsTab.azureModelCount', {
186785
187340
  count: azureModels.length,
186786
187341
  }) })) : (String((_c = cred === null || cred === void 0 ? void 0 : cred.value) === null || _c === void 0 ? void 0 : _c.key).slice(0, 15) || '') }), jsx("td", { className: "px-6 py-4", children: jsx(Button$1, { variant: "ghost", size: "sm", className: "cursor-pointer mr-2", disabled: isDeletingIntegration || isDeletingCredential, onClick: () => handleDeleteCredential(cred.name, cred.platform, false), children: jsx(Trash, { className: "h-4 w-4" }) }) })] }), isAzure &&
186787
187342
  isExpanded &&
@@ -189547,7 +190102,7 @@ function ItemSearchDropdown({ platformKey, onSelect, }) {
189547
190102
  }, children: [jsx("div", { className: "flex-1 min-w-0", children: jsx("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100 block truncate", children: item.name }) }), jsx(Badge, { variant: "secondary", className: "text-xs shrink-0 capitalize", children: displayItemType(item.itemType) })] }, `${item.itemType}-${item.id}`)))) : isFetching ? (jsx("div", { className: "px-3 py-4 text-center text-sm text-gray-400", children: t('monetizationItemSearchDropdown.searching') })) : (jsx("div", { className: "px-3 py-4 text-center text-sm text-gray-400", children: t('monetizationItemSearchDropdown.noItemsFound') })) }))] }));
189548
190103
  }
189549
190104
 
189550
- const PAGE_SIZE$6 = 8;
190105
+ const PAGE_SIZE$7 = 8;
189551
190106
  function PaywalledItemsList({ platformKey, onSelectItem, onAddCustomItem, }) {
189552
190107
  const t = useT();
189553
190108
  const [page, setPage] = useState(1);
@@ -189555,7 +190110,7 @@ function PaywalledItemsList({ platformKey, onSelectItem, onAddCustomItem, }) {
189555
190110
  const { data: paywalls, isLoading } = useListPaywallsQuery({
189556
190111
  platform_key: platformKey,
189557
190112
  page,
189558
- page_size: PAGE_SIZE$6,
190113
+ page_size: PAGE_SIZE$7,
189559
190114
  ...(statusFilter !== 'all' && { is_enabled: statusFilter === 'active' }),
189560
190115
  }, { refetchOnMountOrArgChange: true });
189561
190116
  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) => {
@@ -189578,7 +190133,7 @@ function PaywalledItemsList({ platformKey, onSelectItem, onAddCustomItem, }) {
189578
190133
  : 'bg-gray-100 text-gray-500 border-gray-200 hover:bg-gray-100', children: pw.is_enabled
189579
190134
  ? t('monetizationPaywalledItemsList.statusActive')
189580
190135
  : t('monetizationPaywalledItemsList.statusDisabled') })] }) }, `${pw.item_type}-${pw.item_id}`));
189581
- }), jsx(AdvancedPagination, { variant: "footer", className: "mt-2 rounded-lg border", currentPage: page, totalPages: Math.ceil(paywalls.count / PAGE_SIZE$6), onPageChange: setPage, totalCount: paywalls.count, itemsPerPage: PAGE_SIZE$6, itemLabel: "items" })] })) : (jsx("p", { className: "text-sm text-gray-400 text-center py-6", children: statusFilter !== 'all'
190136
+ }), jsx(AdvancedPagination, { variant: "footer", className: "mt-2 rounded-lg border", currentPage: page, totalPages: Math.ceil(paywalls.count / PAGE_SIZE$7), onPageChange: setPage, totalCount: paywalls.count, itemsPerPage: PAGE_SIZE$7, itemLabel: "items" })] })) : (jsx("p", { className: "text-sm text-gray-400 text-center py-6", children: statusFilter !== 'all'
189582
190137
  ? t('monetizationPaywalledItemsList.noFilteredItemsFound', { status: statusFilter })
189583
190138
  : t('monetizationPaywalledItemsList.noItemsConfigured') }))] })] }));
189584
190139
  }
@@ -251993,6 +252548,147 @@ function Markdown({ children, className }) {
251993
252548
  const AnalyticsSettingsContext = createContext({});
251994
252549
  AnalyticsSettingsContext.Provider;
251995
252550
 
252551
+ /**
252552
+ * Core agent search hook — thin wrapper around `useGetAiSearchMentorsQuery`.
252553
+ *
252554
+ * This is the low-level building block. For a higher-level hook that manages
252555
+ * search state, debounce, and pagination controls, use
252556
+ * `useAgentSearchWithPagination`.
252557
+ */
252558
+ function useAgentSearch({ tenantKey, query, limit = 6, offset, starred, featured, category, subjects, llm, types, includeMainPublicMentors, orderDirection, skip = false, }) {
252559
+ var _a, _b, _c;
252560
+ const { data, isLoading, isFetching, refetch } = useGetAiSearchMentorsQuery({
252561
+ platform_key: tenantKey,
252562
+ query: query || undefined,
252563
+ limit,
252564
+ ...(offset !== undefined && { offset }),
252565
+ ...(starred !== undefined && { starred }),
252566
+ ...(featured !== undefined && { featured }),
252567
+ ...(category && { category }),
252568
+ ...(subjects && { subjects }),
252569
+ ...(llm && { llm }),
252570
+ ...(types && { types }),
252571
+ ...(includeMainPublicMentors !== undefined && {
252572
+ include_main_public_mentors: includeMainPublicMentors,
252573
+ }),
252574
+ ...(orderDirection && { order_direction: orderDirection }),
252575
+ }, { skip: skip || !tenantKey });
252576
+ return {
252577
+ agents: ((_a = data === null || data === void 0 ? void 0 : data.results) !== null && _a !== void 0 ? _a : []),
252578
+ facets: data === null || data === void 0 ? void 0 : data.facets,
252579
+ hasMore: !!(data === null || data === void 0 ? void 0 : data.next),
252580
+ totalCount: (_b = data === null || data === void 0 ? void 0 : data.count) !== null && _b !== void 0 ? _b : 0,
252581
+ totalPages: (_c = data === null || data === void 0 ? void 0 : data.total_pages) !== null && _c !== void 0 ? _c : 0,
252582
+ isLoading,
252583
+ isFetching,
252584
+ refetch,
252585
+ };
252586
+ }
252587
+
252588
+ function c$3(e,u,c){var i=this,a=useRef(null),o=useRef(0),f=useRef(null),l=useRef([]),v=useRef(),m=useRef(),d=useRef(e),g=useRef(true);d.current=e;var p="undefined"!=typeof window,w=!u&&0!==u&&p;if("function"!=typeof e)throw new TypeError("Expected a function");u=+u||0;var s=!!(c=c||{}).leading,x=!("trailing"in c)||!!c.trailing,h="maxWait"in c,y="debounceOnServer"in c&&!!c.debounceOnServer,F=h?Math.max(+c.maxWait||0,u):null;useEffect(function(){return g.current=true,function(){g.current=false;}},[]);var A=useMemo(function(){var r=function(r){var n=l.current,t=v.current;return l.current=v.current=null,o.current=r,m.current=d.current.apply(t,n)},n=function(r,n){w&&cancelAnimationFrame(f.current),f.current=w?requestAnimationFrame(r):setTimeout(r,n);},t=function(r){if(!g.current)return false;var n=r-a.current;return !a.current||n>=u||n<0||h&&r-o.current>=F},e=function(n){return f.current=null,x&&l.current?r(n):(l.current=v.current=null,m.current)},c=function r(){var c=Date.now();if(t(c))return e(c);if(g.current){var i=u-(c-a.current),f=h?Math.min(i,F-(c-o.current)):i;n(r,f);}},A=function(){if(p||y){var e=Date.now(),d=t(e);if(l.current=[].slice.call(arguments),v.current=i,a.current=e,d){if(!f.current&&g.current)return o.current=a.current,n(c,u),s?r(a.current):m.current;if(h)return n(c,u),r(a.current)}return f.current||n(c,u),m.current}};return A.cancel=function(){f.current&&(w?cancelAnimationFrame(f.current):clearTimeout(f.current)),o.current=0,l.current=a.current=v.current=f.current=null;},A.isPending=function(){return !!f.current},A.flush=function(){return f.current?e(Date.now()):m.current},A},[s,h,u,F,x,w,p,y]);return A}function i$8(r,n){return r===n}function a$3(n,t,a){var o=i$8,f=useRef(n),l=useState({})[1],v=c$3(useCallback(function(r){f.current=r,l({});},[l]),t,a),m=useRef(n);return o(m.current,n)||(v(n),m.current=n),[f.current,v]}
252589
+
252590
+ /**
252591
+ * Higher-level agent search hook that manages search state, debounce, and
252592
+ * pagination controls on top of `useAgentSearch`.
252593
+ *
252594
+ * Use this for simple list/table views (MyMentorsModal, SettingsModal).
252595
+ * For the full explore page with sections, use `useAgentSearch` directly.
252596
+ */
252597
+ function useAgentSearchWithPagination({ itemsPerPage = 6, paginationMode = 'paginated', debounceMs = 500, ...queryConfig }) {
252598
+ const [searchQuery, setSearchQuery] = React__default.useState('');
252599
+ const [debouncedQuery] = a$3(searchQuery, debounceMs);
252600
+ const [currentPage, setCurrentPage] = React__default.useState(1);
252601
+ const [infiniteLimit, setInfiniteLimit] = React__default.useState(itemsPerPage);
252602
+ // Reset pagination when search changes
252603
+ React__default.useEffect(() => {
252604
+ setCurrentPage(1);
252605
+ setInfiniteLimit(itemsPerPage);
252606
+ }, [debouncedQuery, itemsPerPage]);
252607
+ const isPaginated = paginationMode === 'paginated';
252608
+ const result = useAgentSearch({
252609
+ ...queryConfig,
252610
+ query: debouncedQuery,
252611
+ limit: isPaginated ? itemsPerPage : infiniteLimit,
252612
+ offset: isPaginated ? (currentPage - 1) * itemsPerPage : undefined,
252613
+ });
252614
+ const totalPages = Math.ceil(result.totalCount / itemsPerPage);
252615
+ const handlePageChange = React__default.useCallback((page) => {
252616
+ setCurrentPage(page);
252617
+ }, []);
252618
+ const loadMore = React__default.useCallback(() => {
252619
+ setInfiniteLimit((prev) => prev + itemsPerPage);
252620
+ }, [itemsPerPage]);
252621
+ return {
252622
+ ...result,
252623
+ searchQuery,
252624
+ setSearchQuery,
252625
+ debouncedQuery,
252626
+ currentPage,
252627
+ totalPages,
252628
+ handlePageChange,
252629
+ loadMore,
252630
+ };
252631
+ }
252632
+
252633
+ /**
252634
+ * Hook for starring / un-starring agents.
252635
+ *
252636
+ * Extracted so the star logic can be shared by any component that renders
252637
+ * agent cards with a favorite toggle.
252638
+ */
252639
+ function useAgentStar({ tenantKey, username, onUnauthenticatedAction, }) {
252640
+ const [starAgent] = useStarMentorMutation();
252641
+ const [unstarAgent] = useUnstarMentorMutation();
252642
+ const [togglingAgentId, setTogglingAgentId] = React__default.useState(null);
252643
+ const toggleFavorite = React__default.useCallback(async (agent, event) => {
252644
+ event.stopPropagation();
252645
+ if (!username) {
252646
+ onUnauthenticatedAction === null || onUnauthenticatedAction === void 0 ? void 0 : onUnauthenticatedAction();
252647
+ return;
252648
+ }
252649
+ if (!tenantKey)
252650
+ return;
252651
+ setTogglingAgentId(String(agent.id));
252652
+ try {
252653
+ const mutation = agent.starred ? unstarAgent : starAgent;
252654
+ await mutation({
252655
+ org: tenantKey,
252656
+ // @ts-expect-error userId param type mismatch in data-layer
252657
+ userId: username,
252658
+ mentor: agent.unique_id || '',
252659
+ }).unwrap();
252660
+ }
252661
+ catch (error) {
252662
+ console.error('Error toggling favorite:', error);
252663
+ }
252664
+ finally {
252665
+ setTogglingAgentId(null);
252666
+ }
252667
+ }, [username, tenantKey, starAgent, unstarAgent, onUnauthenticatedAction]);
252668
+ return { toggleFavorite, togglingAgentId };
252669
+ }
252670
+
252671
+ function MentorSelectionGrid({ selectedMentorIds, onMentorSelect, searchQuery, onSearchChange, tenantKey, itemsPerPage = 8, showSearch = true, minHeight = '400px', }) {
252672
+ const t = useT();
252673
+ const { agents: mentors, isLoading: isMentorsLoading, isFetching: isMentorsFetching, currentPage, totalPages, setSearchQuery: setMentorSearch, handlePageChange, } = useAgentSearchWithPagination({ tenantKey, itemsPerPage });
252674
+ // Update search query with debounce effect handled by the hook
252675
+ React__default.useEffect(() => {
252676
+ setMentorSearch(searchQuery);
252677
+ }, [searchQuery]);
252678
+ const isMentorSelected = (mentorId) => {
252679
+ return selectedMentorIds.includes(mentorId);
252680
+ };
252681
+ return (jsxs("div", { className: "space-y-4", children: [showSearch && (jsxs("div", { className: "relative", children: [isMentorsFetching ? (jsx(Spinner, { className: "absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 transform text-gray-400" })) : (jsx(Search, { className: "absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 transform text-gray-400" })), jsx(Input, { placeholder: t('mentorsMentorSelectionGrid.searchAgentsPlaceholder'), value: searchQuery, onChange: (e) => onSearchChange(e.target.value), className: "h-10 border-gray-200 pl-10 focus:border-blue-500 focus:ring-0" })] })), jsxs("div", { className: "space-y-4", children: [jsx("div", { className: "grid max-h-96 auto-rows-min grid-cols-1 items-start gap-3 overflow-y-auto rounded-lg border border-gray-200 p-4 md:max-h-none md:grid-cols-2", style: { minHeight }, children: isMentorsLoading ? (jsx("div", { className: "col-span-full py-8 text-center text-gray-500", children: t('mentorsMentorSelectionGrid.loadingAgents') })) : mentors.length === 0 ? (jsx("div", { className: "col-span-full py-8 text-center text-gray-500", children: t('mentorsMentorSelectionGrid.noAgentsFound') })) : (mentors.map((mentor) => {
252682
+ const isSelected = isMentorSelected(mentor.unique_id || '');
252683
+ const isAlreadyAdded = selectedMentorIds.includes(mentor.unique_id || '');
252684
+ return (jsxs("button", { onClick: () => onMentorSelect(mentor), className: `flex cursor-pointer items-center gap-3 rounded-lg border-2 p-3 transition-all ${isSelected
252685
+ ? 'border-blue-500 bg-blue-50'
252686
+ : isAlreadyAdded
252687
+ ? 'bg-blue-25 border-blue-300'
252688
+ : 'border-gray-200 hover:border-gray-300 hover:bg-gray-50'}`, children: [jsxs(Avatar, { className: "h-12 w-12 flex-shrink-0", children: [jsx(AvatarImage, { src: mentor.profile_image || '/placeholder.svg', alt: mentor.name }), jsx(AvatarFallback, { children: mentor.name.substring(0, 2) })] }), jsxs("div", { className: "flex min-w-0 flex-1 flex-col justify-start text-left", children: [jsx("h4", { className: "truncate text-sm font-medium text-gray-900", children: mentor.name }), jsx("p", { className: "line-clamp-2 text-xs text-gray-600", children: mentor.description })] }), isSelected && (jsx("div", { className: "flex-shrink-0", children: jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-full bg-blue-600", children: jsx(Check, { className: "h-4 w-4 text-white" }) }) })), isAlreadyAdded && !isSelected && (jsx("div", { className: "flex-shrink-0", children: jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-full bg-blue-400", children: jsx(Check, { className: "h-4 w-4 text-white" }) }) }))] }, mentor.unique_id));
252689
+ })) }), jsx(IblPagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: handlePageChange, disabled: isMentorsFetching || isMentorsLoading })] })] }));
252690
+ }
252691
+
251996
252692
  const SkeletonMultiplier = ({ Skeleton, multiplier }) => {
251997
252693
  return (jsx(Fragment$1, { children: Array.from({ length: multiplier }).map((_, index) => (jsx(Skeleton, {}, index))) }));
251998
252694
  };
@@ -253141,52 +253837,6 @@ var reactPaginate = {exports: {}};
253141
253837
 
253142
253838
  } (reactPaginate));
253143
253839
 
253144
- /**
253145
- * Returns the canonical logo path and display name for an LLM provider.
253146
- * `logo` is a static-root path (e.g. `/llm-amazon-provider.png`); the package
253147
- * ships the canonical assets under `@iblai/web-containers/public/` — copy them
253148
- * into your app's static-root (`public/` for Next.js) so the paths resolve.
253149
- */
253150
- function getLLMProviderDetails(llmProvider, llmName) {
253151
- switch (llmProvider) {
253152
- case 'groq':
253153
- return { logo: '/llm-groq-provider.png', name: 'Groq' };
253154
- case 'azure_openai':
253155
- return { logo: '/llm-microsoft-provider.png', name: 'Microsoft' };
253156
- case 'openai':
253157
- if (llmName)
253158
- return { logo: '/llm-openai-provider.jpg', name: 'OpenAI' };
253159
- return { logo: '/llm-openai-provider-2.svg', name: 'OpenAI' };
253160
- case 'mistral':
253161
- return { logo: '/llm-mistral-provider.jpeg', name: 'Mistral' };
253162
- case 'google':
253163
- if (llmName)
253164
- return { logo: '/llm-gemini-provider.png', name: 'Google' };
253165
- return { logo: '/llm-google-provider.svg', name: 'Google' };
253166
- case 'llama':
253167
- return { logo: '/llm-llama-provider.jpeg', name: 'Meta' };
253168
- case 'perplexity':
253169
- return { logo: '/llm-perplexity-provider.webp', name: 'Perplexity' };
253170
- case 'deepseek':
253171
- return { logo: '/llm-deepseek-provider.png', name: 'DeepSeek' };
253172
- case 'xai':
253173
- return { logo: '/llm-xai-provider.jpg', name: 'xAI' };
253174
- case 'IBLChatAnthropic':
253175
- case 'anthropic':
253176
- return { logo: '/llm-claude-provider.png', name: 'Anthropic' };
253177
- case 'IBLChatNvidia':
253178
- case 'nvidia':
253179
- return { logo: '/llm-nvidia-provider.webp', name: 'NVIDIA' };
253180
- case 'bedrock':
253181
- case 'amazon-bedrock':
253182
- case 'amazon_bedrock':
253183
- case 'IBLChatBedrock':
253184
- return { logo: '/llm-amazon-provider.png', name: 'Amazon' };
253185
- default:
253186
- return { logo: '/llm-generic-provider.png', name: llmProvider };
253187
- }
253188
- }
253189
-
253190
253840
  function OverlayModal({ open, onClose, title, description, className, children, }) {
253191
253841
  const t = useT();
253192
253842
  const [mounted, setMounted] = useState(false);
@@ -253724,8 +254374,6 @@ function useSlashSkillPicker({ inputValue, setInputValue, skills, isLoading = fa
253724
254374
  };
253725
254375
  }
253726
254376
 
253727
- function c$3(e,u,c){var i=this,a=useRef(null),o=useRef(0),f=useRef(null),l=useRef([]),v=useRef(),m=useRef(),d=useRef(e),g=useRef(true);d.current=e;var p="undefined"!=typeof window,w=!u&&0!==u&&p;if("function"!=typeof e)throw new TypeError("Expected a function");u=+u||0;var s=!!(c=c||{}).leading,x=!("trailing"in c)||!!c.trailing,h="maxWait"in c,y="debounceOnServer"in c&&!!c.debounceOnServer,F=h?Math.max(+c.maxWait||0,u):null;useEffect(function(){return g.current=true,function(){g.current=false;}},[]);var A=useMemo(function(){var r=function(r){var n=l.current,t=v.current;return l.current=v.current=null,o.current=r,m.current=d.current.apply(t,n)},n=function(r,n){w&&cancelAnimationFrame(f.current),f.current=w?requestAnimationFrame(r):setTimeout(r,n);},t=function(r){if(!g.current)return false;var n=r-a.current;return !a.current||n>=u||n<0||h&&r-o.current>=F},e=function(n){return f.current=null,x&&l.current?r(n):(l.current=v.current=null,m.current)},c=function r(){var c=Date.now();if(t(c))return e(c);if(g.current){var i=u-(c-a.current),f=h?Math.min(i,F-(c-o.current)):i;n(r,f);}},A=function(){if(p||y){var e=Date.now(),d=t(e);if(l.current=[].slice.call(arguments),v.current=i,a.current=e,d){if(!f.current&&g.current)return o.current=a.current,n(c,u),s?r(a.current):m.current;if(h)return n(c,u),r(a.current)}return f.current||n(c,u),m.current}};return A.cancel=function(){f.current&&(w?cancelAnimationFrame(f.current):clearTimeout(f.current)),o.current=0,l.current=a.current=v.current=f.current=null;},A.isPending=function(){return !!f.current},A.flush=function(){return f.current?e(Date.now()):m.current},A},[s,h,u,F,x,w,p,y]);return A}function i$8(r,n){return r===n}function a$3(n,t,a){var o=i$8,f=useRef(n),l=useState({})[1],v=c$3(useCallback(function(r){f.current=r,l({});},[l]),t,a),m=useRef(n);return o(m.current,n)||(v(n),m.current=n),[f.current,v]}
253728
-
253729
254377
  lazy(() => Promise.resolve().then(function () { return createProjectModal; }).then((mod) => ({
253730
254378
  default: mod.CreateProjectModal,
253731
254379
  })));
@@ -254561,7 +255209,7 @@ function RetrievedDocumentsButton({ sessionId }) {
254561
255209
  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 }) })] }) })] }));
254562
255210
  }
254563
255211
 
254564
- const PAGE_SIZE$5 = 10;
255212
+ const PAGE_SIZE$6 = 10;
254565
255213
  const MemoryMenu = ({ onClose, mentorId, tenantKey, username }) => {
254566
255214
  const t = useT();
254567
255215
  const [page, setPage] = useState(1);
@@ -254579,7 +255227,7 @@ const MemoryMenu = ({ onClose, mentorId, tenantKey, username }) => {
254579
255227
  mentorId,
254580
255228
  params: {
254581
255229
  page,
254582
- page_size: PAGE_SIZE$5,
255230
+ page_size: PAGE_SIZE$6,
254583
255231
  my_memory: true,
254584
255232
  },
254585
255233
  }, {
@@ -256129,145 +256777,6 @@ var minimumMentorAlert = /*#__PURE__*/Object.freeze({
256129
256777
  MinimumMentorAlert: MinimumMentorAlert
256130
256778
  });
256131
256779
 
256132
- /**
256133
- * Core agent search hook — thin wrapper around `useGetAiSearchMentorsQuery`.
256134
- *
256135
- * This is the low-level building block. For a higher-level hook that manages
256136
- * search state, debounce, and pagination controls, use
256137
- * `useAgentSearchWithPagination`.
256138
- */
256139
- function useAgentSearch({ tenantKey, query, limit = 6, offset, starred, featured, category, subjects, llm, types, includeMainPublicMentors, orderDirection, skip = false, }) {
256140
- var _a, _b, _c;
256141
- const { data, isLoading, isFetching, refetch } = useGetAiSearchMentorsQuery({
256142
- platform_key: tenantKey,
256143
- query: query || undefined,
256144
- limit,
256145
- ...(offset !== undefined && { offset }),
256146
- ...(starred !== undefined && { starred }),
256147
- ...(featured !== undefined && { featured }),
256148
- ...(category && { category }),
256149
- ...(subjects && { subjects }),
256150
- ...(llm && { llm }),
256151
- ...(types && { types }),
256152
- ...(includeMainPublicMentors !== undefined && {
256153
- include_main_public_mentors: includeMainPublicMentors,
256154
- }),
256155
- ...(orderDirection && { order_direction: orderDirection }),
256156
- }, { skip: skip || !tenantKey });
256157
- return {
256158
- agents: ((_a = data === null || data === void 0 ? void 0 : data.results) !== null && _a !== void 0 ? _a : []),
256159
- facets: data === null || data === void 0 ? void 0 : data.facets,
256160
- hasMore: !!(data === null || data === void 0 ? void 0 : data.next),
256161
- totalCount: (_b = data === null || data === void 0 ? void 0 : data.count) !== null && _b !== void 0 ? _b : 0,
256162
- totalPages: (_c = data === null || data === void 0 ? void 0 : data.total_pages) !== null && _c !== void 0 ? _c : 0,
256163
- isLoading,
256164
- isFetching,
256165
- refetch,
256166
- };
256167
- }
256168
-
256169
- /**
256170
- * Higher-level agent search hook that manages search state, debounce, and
256171
- * pagination controls on top of `useAgentSearch`.
256172
- *
256173
- * Use this for simple list/table views (MyMentorsModal, SettingsModal).
256174
- * For the full explore page with sections, use `useAgentSearch` directly.
256175
- */
256176
- function useAgentSearchWithPagination({ itemsPerPage = 6, paginationMode = 'paginated', debounceMs = 500, ...queryConfig }) {
256177
- const [searchQuery, setSearchQuery] = React__default.useState('');
256178
- const [debouncedQuery] = a$3(searchQuery, debounceMs);
256179
- const [currentPage, setCurrentPage] = React__default.useState(1);
256180
- const [infiniteLimit, setInfiniteLimit] = React__default.useState(itemsPerPage);
256181
- // Reset pagination when search changes
256182
- React__default.useEffect(() => {
256183
- setCurrentPage(1);
256184
- setInfiniteLimit(itemsPerPage);
256185
- }, [debouncedQuery, itemsPerPage]);
256186
- const isPaginated = paginationMode === 'paginated';
256187
- const result = useAgentSearch({
256188
- ...queryConfig,
256189
- query: debouncedQuery,
256190
- limit: isPaginated ? itemsPerPage : infiniteLimit,
256191
- offset: isPaginated ? (currentPage - 1) * itemsPerPage : undefined,
256192
- });
256193
- const totalPages = Math.ceil(result.totalCount / itemsPerPage);
256194
- const handlePageChange = React__default.useCallback((page) => {
256195
- setCurrentPage(page);
256196
- }, []);
256197
- const loadMore = React__default.useCallback(() => {
256198
- setInfiniteLimit((prev) => prev + itemsPerPage);
256199
- }, [itemsPerPage]);
256200
- return {
256201
- ...result,
256202
- searchQuery,
256203
- setSearchQuery,
256204
- debouncedQuery,
256205
- currentPage,
256206
- totalPages,
256207
- handlePageChange,
256208
- loadMore,
256209
- };
256210
- }
256211
-
256212
- /**
256213
- * Hook for starring / un-starring agents.
256214
- *
256215
- * Extracted so the star logic can be shared by any component that renders
256216
- * agent cards with a favorite toggle.
256217
- */
256218
- function useAgentStar({ tenantKey, username, onUnauthenticatedAction, }) {
256219
- const [starAgent] = useStarMentorMutation();
256220
- const [unstarAgent] = useUnstarMentorMutation();
256221
- const [togglingAgentId, setTogglingAgentId] = React__default.useState(null);
256222
- const toggleFavorite = React__default.useCallback(async (agent, event) => {
256223
- event.stopPropagation();
256224
- if (!username) {
256225
- onUnauthenticatedAction === null || onUnauthenticatedAction === void 0 ? void 0 : onUnauthenticatedAction();
256226
- return;
256227
- }
256228
- if (!tenantKey)
256229
- return;
256230
- setTogglingAgentId(String(agent.id));
256231
- try {
256232
- const mutation = agent.starred ? unstarAgent : starAgent;
256233
- await mutation({
256234
- org: tenantKey,
256235
- // @ts-expect-error userId param type mismatch in data-layer
256236
- userId: username,
256237
- mentor: agent.unique_id || '',
256238
- }).unwrap();
256239
- }
256240
- catch (error) {
256241
- console.error('Error toggling favorite:', error);
256242
- }
256243
- finally {
256244
- setTogglingAgentId(null);
256245
- }
256246
- }, [username, tenantKey, starAgent, unstarAgent, onUnauthenticatedAction]);
256247
- return { toggleFavorite, togglingAgentId };
256248
- }
256249
-
256250
- function MentorSelectionGrid({ selectedMentorIds, onMentorSelect, searchQuery, onSearchChange, tenantKey, itemsPerPage = 8, showSearch = true, minHeight = '400px', }) {
256251
- const t = useT();
256252
- const { agents: mentors, isLoading: isMentorsLoading, isFetching: isMentorsFetching, currentPage, totalPages, setSearchQuery: setMentorSearch, handlePageChange, } = useAgentSearchWithPagination({ tenantKey, itemsPerPage });
256253
- // Update search query with debounce effect handled by the hook
256254
- React__default.useEffect(() => {
256255
- setMentorSearch(searchQuery);
256256
- }, [searchQuery]);
256257
- const isMentorSelected = (mentorId) => {
256258
- return selectedMentorIds.includes(mentorId);
256259
- };
256260
- return (jsxs("div", { className: "space-y-4", children: [showSearch && (jsxs("div", { className: "relative", children: [isMentorsFetching ? (jsx(Spinner, { className: "absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 transform text-gray-400" })) : (jsx(Search, { className: "absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 transform text-gray-400" })), jsx(Input, { placeholder: t('mentorsMentorSelectionGrid.searchAgentsPlaceholder'), value: searchQuery, onChange: (e) => onSearchChange(e.target.value), className: "h-10 border-gray-200 pl-10 focus:border-blue-500 focus:ring-0" })] })), jsxs("div", { className: "space-y-4", children: [jsx("div", { className: "grid max-h-96 auto-rows-min grid-cols-1 items-start gap-3 overflow-y-auto rounded-lg border border-gray-200 p-4 md:max-h-none md:grid-cols-2", style: { minHeight }, children: isMentorsLoading ? (jsx("div", { className: "col-span-full py-8 text-center text-gray-500", children: t('mentorsMentorSelectionGrid.loadingAgents') })) : mentors.length === 0 ? (jsx("div", { className: "col-span-full py-8 text-center text-gray-500", children: t('mentorsMentorSelectionGrid.noAgentsFound') })) : (mentors.map((mentor) => {
256261
- const isSelected = isMentorSelected(mentor.unique_id || '');
256262
- const isAlreadyAdded = selectedMentorIds.includes(mentor.unique_id || '');
256263
- return (jsxs("button", { onClick: () => onMentorSelect(mentor), className: `flex cursor-pointer items-center gap-3 rounded-lg border-2 p-3 transition-all ${isSelected
256264
- ? 'border-blue-500 bg-blue-50'
256265
- : isAlreadyAdded
256266
- ? 'bg-blue-25 border-blue-300'
256267
- : 'border-gray-200 hover:border-gray-300 hover:bg-gray-50'}`, children: [jsxs(Avatar, { className: "h-12 w-12 flex-shrink-0", children: [jsx(AvatarImage, { src: mentor.profile_image || '/placeholder.svg', alt: mentor.name }), jsx(AvatarFallback, { children: mentor.name.substring(0, 2) })] }), jsxs("div", { className: "flex min-w-0 flex-1 flex-col justify-start text-left", children: [jsx("h4", { className: "truncate text-sm font-medium text-gray-900", children: mentor.name }), jsx("p", { className: "line-clamp-2 text-xs text-gray-600", children: mentor.description })] }), isSelected && (jsx("div", { className: "flex-shrink-0", children: jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-full bg-blue-600", children: jsx(Check, { className: "h-4 w-4 text-white" }) }) })), isAlreadyAdded && !isSelected && (jsx("div", { className: "flex-shrink-0", children: jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-full bg-blue-400", children: jsx(Check, { className: "h-4 w-4 text-white" }) }) }))] }, mentor.unique_id));
256268
- })) }), jsx(IblPagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: handlePageChange, disabled: isMentorsFetching || isMentorsLoading })] })] }));
256269
- }
256270
-
256271
256780
  function CreateProjectModal$1({ isOpen, onClose, tenantKey, username, navigateToMentorInProject, }) {
256272
256781
  const t = useT();
256273
256782
  const dispatch = useDispatch();
@@ -258577,7 +259086,7 @@ function DeleteItemConfirm({ isOpen, onClose, tenantKey, username, datasetName,
258577
259086
  : t('benchmarksDeleteItemConfirm.delete') })] })] }) }));
258578
259087
  }
258579
259088
 
258580
- const PAGE_SIZE$4 = 10;
259089
+ const PAGE_SIZE$5 = 10;
258581
259090
  function BenchmarkItemsModal({ isOpen, onClose, tenantKey, username, datasetName }) {
258582
259091
  var _a;
258583
259092
  const t = useT();
@@ -258589,17 +259098,17 @@ function BenchmarkItemsModal({ isOpen, onClose, tenantKey, username, datasetName
258589
259098
  userId: username,
258590
259099
  datasetName,
258591
259100
  page,
258592
- limit: PAGE_SIZE$4,
259101
+ limit: PAGE_SIZE$5,
258593
259102
  });
258594
259103
  const items = (_a = data === null || data === void 0 ? void 0 : data.results) !== null && _a !== void 0 ? _a : [];
258595
- const totalPages = evalTotalPages(data === null || data === void 0 ? void 0 : data.count, PAGE_SIZE$4);
259104
+ const totalPages = evalTotalPages(data === null || data === void 0 ? void 0 : data.count, PAGE_SIZE$5);
258596
259105
  return (jsx(Dialog, { open: isOpen, onOpenChange: (open) => !open && onClose(), children: jsxs(DialogContent, { "aria-label": "benchmark-items", className: "w-full max-w-4xl max-h-[90vh] overflow-y-auto", children: [jsxs(DialogHeader, { children: [jsx(DialogTitle, { children: datasetName }), jsx(DialogDescription, { children: t('benchmarksBenchmarkItemsModal.description') })] }), jsx("div", { className: "flex justify-end mb-4", children: jsxs(Button$1, { onClick: () => setShowAdd(true), className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] hover:opacity-90 text-white h-[35px]", children: [jsx(Plus, { className: "h-4 w-4 mr-1" }), t('benchmarksBenchmarkItemsModal.addItems')] }) }), isLoading ? (jsx("div", { className: "flex justify-center items-center h-40", children: jsx(Spinner, {}) })) : (jsxs(Fragment$1, { children: [jsx("div", { className: "bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg overflow-x-auto", children: jsxs("table", { className: "w-full text-sm", children: [jsx("thead", { className: "bg-gray-50 dark:bg-gray-700 border-b border-gray-200 dark:border-gray-600", children: jsxs("tr", { children: [jsx("th", { className: "px-4 py-3 text-left font-medium text-gray-500 dark:text-gray-400 w-[45%]", children: t('benchmarksBenchmarkItemsModal.questionColumn') }), jsx("th", { className: "px-4 py-3 text-left font-medium text-gray-500 dark:text-gray-400 w-[45%]", children: t('benchmarksBenchmarkItemsModal.answerColumn') }), jsx("th", { className: "px-4 py-3 text-right font-medium text-gray-500 dark:text-gray-400 w-[10%]", children: t('benchmarksBenchmarkItemsModal.actionsColumn') })] }) }), jsxs("tbody", { className: "divide-y divide-gray-200 dark:divide-gray-600", children: [items.map((item) => {
258597
259106
  var _a;
258598
259107
  return (jsxs("tr", { className: "align-top", children: [jsx("td", { className: "px-4 py-3 text-gray-900 dark:text-gray-100", children: jsx("div", { className: "line-clamp-3 break-words", children: typeof item.input === 'string' ? item.input : JSON.stringify(item.input) }) }), jsx("td", { className: "px-4 py-3 text-gray-600 dark:text-gray-300", children: jsx("div", { className: "line-clamp-3 break-words", children: (_a = item.expected_output) !== null && _a !== void 0 ? _a : jsx("span", { className: "text-gray-400", children: "\u2014" }) }) }), jsx("td", { className: "px-4 py-3 text-right", children: jsx(Button$1, { variant: "ghost", size: "sm", "aria-label": t('benchmarksBenchmarkItemsModal.deleteItemAriaLabel'), onClick: () => setDeleteItemId(item.id), children: jsx(Trash2, { className: "h-4 w-4 text-red-500" }) }) })] }, item.id));
258599
259108
  }), items.length === 0 && (jsx("tr", { children: jsx("td", { colSpan: 3, className: "px-4 py-8 text-center text-gray-500 dark:text-gray-400", children: t('benchmarksBenchmarkItemsModal.empty') }) }))] })] }) }), totalPages > 1 && (jsx("div", { className: "mt-6", children: jsx(AdvancedPagination, { totalPages: totalPages, currentPage: page, onPageChange: setPage }) }))] })), jsx(AddItemsModal, { isOpen: showAdd, onClose: () => setShowAdd(false), tenantKey: tenantKey, username: username, datasetName: datasetName }), deleteItemId && (jsx(DeleteItemConfirm, { isOpen: !!deleteItemId, onClose: () => setDeleteItemId(null), tenantKey: tenantKey, username: username, datasetName: datasetName, itemId: deleteItemId }))] }) }));
258600
259109
  }
258601
259110
 
258602
- const PAGE_SIZE$3 = 10;
259111
+ const PAGE_SIZE$4 = 10;
258603
259112
  function BenchmarksTab({ tenantKey, username, }) {
258604
259113
  var _a;
258605
259114
  const t = useT();
@@ -258611,13 +259120,13 @@ function BenchmarksTab({ tenantKey, username, }) {
258611
259120
  org: tenantKey,
258612
259121
  userId: username,
258613
259122
  page,
258614
- limit: PAGE_SIZE$3,
259123
+ limit: PAGE_SIZE$4,
258615
259124
  });
258616
259125
  const benchmarks = (_a = data === null || data === void 0 ? void 0 : data.results) !== null && _a !== void 0 ? _a : [];
258617
259126
  const filtered = searchQuery.trim()
258618
259127
  ? benchmarks.filter((benchmark) => benchmark.name.toLowerCase().includes(searchQuery.toLowerCase()))
258619
259128
  : benchmarks;
258620
- const totalPages = evalTotalPages(data === null || data === void 0 ? void 0 : data.count, PAGE_SIZE$3);
259129
+ const totalPages = evalTotalPages(data === null || data === void 0 ? void 0 : data.count, PAGE_SIZE$4);
258621
259130
  return (jsxs("div", { className: "mt-4", children: [jsxs("div", { className: "flex justify-between items-center gap-1 flex-wrap", children: [jsxs("div", { className: "relative mb-6 max-w-sm w-full sm:w-auto", children: [jsx(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400" }), jsx(Input, { type: "text", value: searchQuery, onChange: (event) => setSearchQuery(event.target.value), placeholder: t('benchmarksIndex.searchPlaceholder'), className: "pl-10 pr-10 focus:ring-blue-500 focus:border-blue-500 h-[35px]", "aria-label": t('benchmarksIndex.searchAriaLabel') })] }), jsx("div", { className: "mb-6", children: jsxs(Button$1, { className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] hover:opacity-90 text-white h-[35px]", onClick: () => setShowCreate(true), children: [jsx(Plus, { className: "h-4 w-4 mr-1" }), t('benchmarksIndex.newBenchmark')] }) })] }), isLoading ? (jsx("div", { className: "flex justify-center items-center h-64", children: jsx(Spinner, {}) })) : (jsxs(Fragment$1, { children: [jsx("div", { className: "bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg overflow-x-auto", children: jsxs("table", { className: "w-full text-sm", children: [jsx("thead", { className: "bg-gray-50 dark:bg-gray-700 border-b border-gray-200 dark:border-gray-600", children: jsxs("tr", { children: [jsx("th", { className: "px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400 w-[28%]", children: t('benchmarksIndex.nameColumn') }), jsx("th", { className: "px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400 w-[36%]", children: t('benchmarksIndex.descriptionColumn') }), jsx("th", { className: "px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400 w-[20%]", children: t('benchmarksIndex.createdColumn') }), jsx("th", { className: "px-6 py-3 text-right font-medium text-gray-500 dark:text-gray-400 w-[16%]", children: t('benchmarksIndex.actionsColumn') })] }) }), jsxs("tbody", { className: "divide-y divide-gray-200 dark:divide-gray-600", children: [filtered.map((benchmark) => (jsxs("tr", { children: [jsx("td", { className: "px-6 py-4 text-gray-900 dark:text-gray-100 max-w-0", children: jsx("div", { className: "truncate font-medium", children: benchmark.name }) }), jsx("td", { className: "px-6 py-4 text-gray-600 dark:text-gray-300 max-w-0", children: jsx("div", { className: "truncate", children: benchmark.description || '—' }) }), jsx("td", { className: "px-6 py-4 text-gray-600 dark:text-gray-300 whitespace-nowrap", children: benchmark.created_at
258622
259131
  ? formatDistanceToNow(new Date(benchmark.created_at), {
258623
259132
  addSuffix: true,
@@ -262487,7 +262996,7 @@ function PlatformSidebarFooter({ collapsed, config, }) {
262487
262996
  * defined for completeness so adding "Organization" later is a one-line
262488
262997
  * change.
262489
262998
  */
262490
- function PlatformAccountSheet({ tab, onClose, tenantKey, username, email, onInviteClick, mainPlatformKey, authUrl, currentSpa, platformBaseDomain, }) {
262999
+ function PlatformAccountSheet({ tab, onClose, tenantKey, username, email, onInviteClick, mainPlatformKey, authUrl, currentSpa, platformBaseDomain, showGradebookTab = false, }) {
262491
263000
  const t = useT();
262492
263001
  const open = tab !== null;
262493
263002
  const accountTabTitles = {
@@ -262510,7 +263019,7 @@ function PlatformAccountSheet({ tab, onClose, tenantKey, username, email, onInvi
262510
263019
  // RBAC is off for this entry point — admin users see every
262511
263020
  // sub-tab. Tenant-level access is already gated by the
262512
263021
  // sidebar footer visibility rules.
262513
- hasUserTabPermission: true, hasGroupsTabPermission: true, hasRolesTabPermission: true, hasPoliciesTabPermission: true, hasTeamsTabPermission: true, hasAlertsTabPermission: true, hasInviteUserPermission: true, hasCreateTeamPermission: true, enableRbac: false, rbacPermissions: {} })), tab === 'integrations' && jsx(IntegrationsTab, { tenantKey: tenantKey, username: username }), tab === 'billing' && (jsx(BillingTab, { tenant: tenantKey, username: username, mainPlatformKey: mainPlatformKey, currentUserEmail: email })), tab === 'monetization' && jsx(MonetizationTab, { platformKey: tenantKey, authURL: authUrl }), tab === 'advanced' && (jsx(AdvancedTab, { platformKey: tenantKey, username: username, currentSPA: currentSpa, authURL: authUrl, currentPlatformBaseDomain: platformBaseDomain }))] })] }) }));
263022
+ hasUserTabPermission: true, hasGroupsTabPermission: true, hasRolesTabPermission: true, hasPoliciesTabPermission: true, hasTeamsTabPermission: true, hasAlertsTabPermission: true, hasInviteUserPermission: true, hasCreateTeamPermission: true, enableRbac: false, rbacPermissions: {}, showGradebookTab: showGradebookTab })), tab === 'integrations' && jsx(IntegrationsTab, { tenantKey: tenantKey, username: username }), tab === 'billing' && (jsx(BillingTab, { tenant: tenantKey, username: username, mainPlatformKey: mainPlatformKey, currentUserEmail: email })), tab === 'monetization' && jsx(MonetizationTab, { platformKey: tenantKey, authURL: authUrl }), tab === 'advanced' && (jsx(AdvancedTab, { platformKey: tenantKey, username: username, currentSPA: currentSpa, authURL: authUrl, currentPlatformBaseDomain: platformBaseDomain }))] })] }) }));
262514
263023
  }
262515
263024
 
262516
263025
  /**
@@ -264473,8 +264982,74 @@ function canSwitchProvider(providers, name) {
264473
264982
  const provider = providers.find((p) => p.name === name);
264474
264983
  return !!(provider === null || provider === void 0 ? void 0 : provider.chat_models) && provider.chat_models.length > 0;
264475
264984
  }
264985
+ /**
264986
+ * Whether we hold a usable LLM key for this provider — its own credentials, or
264987
+ * the tenant's main keys when it is allowed to borrow them. An *absent*
264988
+ * `main_has_credentials` means the backend did not report on the main keys and
264989
+ * is treated as available (matching standalone's `@/lib/utils` and
264990
+ * `claw-sandbox`'s copy); only an explicit `false` rules the main keys out.
264991
+ */
264476
264992
  function canSwitchLLm(llm) {
264477
- return !!(llm === null || llm === void 0 ? void 0 : llm.has_credentials) || (!!(llm === null || llm === void 0 ? void 0 : llm.can_use_main_keys) && !!(llm === null || llm === void 0 ? void 0 : llm.main_has_credentials));
264993
+ if (llm === null || llm === void 0 ? void 0 : llm.has_credentials) {
264994
+ return true;
264995
+ }
264996
+ return ((llm === null || llm === void 0 ? void 0 : llm.can_use_main_keys) &&
264997
+ ((llm === null || llm === void 0 ? void 0 : llm.main_has_credentials) === true || (llm === null || llm === void 0 ? void 0 : llm.main_has_credentials) === undefined));
264998
+ }
264999
+ /**
265000
+ * Compares two raw provider names by the label the user actually sees on the
265001
+ * card, not by the backend key — the two diverge often enough to matter
265002
+ * (`bedrock` renders as "Amazon", `azure_openai` as "Microsoft", `iblai` as
265003
+ * "ibl.ai"). Comparison is case-insensitive (`sensitivity: 'base'`) so casing
265004
+ * never produces a surprising order. `getDetails` is the host-provided resolver
265005
+ * the grid renders with, so the sort always matches what is on screen; unknown
265006
+ * providers fall back to their raw name as the display name.
265007
+ */
265008
+ function compareLLMProvidersByDisplayName(a, b, getDetails) {
265009
+ return getDetails(a).name.localeCompare(getDetails(b).name, undefined, {
265010
+ sensitivity: 'base',
265011
+ });
265012
+ }
265013
+ /**
265014
+ * Whether the user can reach *any* model of this provider.
265015
+ *
265016
+ * This is exactly the provider-level half of the per-model `isDisabled` rule in
265017
+ * `llm-tab/llm-provider-modal.tsx`: a model row there is disabled when
265018
+ * `!canSwitchLLm(provider) || !canSwitchProvider(llms, provider.name)`, and both
265019
+ * of those depend only on the provider — so when either fails, *every* model row
265020
+ * of that provider is disabled. `canSwitchProvider` is just "has at least one
265021
+ * `chat_models` entry", which is checked inline here to keep the helper pure and
265022
+ * free of the surrounding provider list.
265023
+ *
265024
+ * `canSwitchLLm` can return `undefined` (its `can_use_main_keys` branch), hence
265025
+ * the explicit `Boolean(...)`.
265026
+ */
265027
+ function canAccessProvider(provider) {
265028
+ var _a, _b;
265029
+ return Boolean(canSwitchLLm(provider !== null && provider !== void 0 ? provider : {})) && ((_b = (_a = provider === null || provider === void 0 ? void 0 : provider.chat_models) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0;
265030
+ }
265031
+ /**
265032
+ * Orders LLM provider cards as two alphabetical groups: providers the user can
265033
+ * actually use (see {@link canAccessProvider}) first, then the ones they can't —
265034
+ * each group sorted alphabetically by display name.
265035
+ *
265036
+ * The grouping predicate is deliberately the same one the grid grays cards with,
265037
+ * so the two always line up: group 1 renders normally, group 2 renders grayed.
265038
+ * Grouping on credentials alone would let a provider that has a key but ships no
265039
+ * chat models sort into the "usable" group while rendering grayed.
265040
+ *
265041
+ * Pure and non-mutating: RTK Query results are frozen, so the input is copied
265042
+ * before sorting. `Array.prototype.sort` is stable, so providers that tie on
265043
+ * both group and display name keep their original relative order.
265044
+ */
265045
+ function sortLLMProvidersByCredentials(providers, getDetails) {
265046
+ return [...providers].sort((a, b) => {
265047
+ const aUsable = canAccessProvider(a);
265048
+ const bUsable = canAccessProvider(b);
265049
+ if (aUsable !== bUsable)
265050
+ return aUsable ? -1 : 1;
265051
+ return compareLLMProvidersByDisplayName(a.name, b.name, getDetails);
265052
+ });
264478
265053
  }
264479
265054
  function extractErrorMessage(error, fallback) {
264480
265055
  var _a, _b;
@@ -264545,12 +265120,16 @@ function LLMProviderModal({ isOpen, onClose, onSelect, llmProvider, isSelecting,
264545
265120
  return (jsxs("button", { disabled: isDisabled, onClick: () => {
264546
265121
  onSelect(llmProvider.name, llm.llm_name);
264547
265122
  }, className: cn('flex cursor-pointer items-center gap-3 rounded-lg border border-gray-200 p-4 transition-colors', {
264548
- 'cursor-not-allowed border-gray-200 bg-white': isDisabled,
265123
+ // Matches the provider grid's inactive treatment: tinted
265124
+ // card, faded logo, full-strength label.
265125
+ 'cursor-not-allowed border-gray-100 bg-gray-50': isDisabled && !isActive,
264549
265126
  'hover:border-blue-500 hover:bg-blue-50': !isDisabled,
264550
265127
  'cursor-not-allowed border-blue-500 bg-blue-50': isActive,
264551
265128
  }), children: [jsx("span", { className: "flex h-8 w-8 items-center justify-center overflow-hidden rounded-lg", children: jsx(Image$4, { src: providerDetails.logo, alt: `${providerDetails.name} icon`, className: cn('h-full w-full object-contain', {
264552
- grayscale: isDisabled && !isActive,
264553
- }), width: 32, height: 32, loading: "lazy" }) }), jsx("span", { className: "text-left text-sm font-medium text-[#646464]", children: llm.llm_name })] }, llm.llm_name));
265129
+ // `grayscale` alone is a no-op on already-black marks,
265130
+ // so fade them as well same rule as the provider grid.
265131
+ 'opacity-40 grayscale': isDisabled && !isActive,
265132
+ }), width: 32, height: 32, loading: "lazy" }) }), jsx("span", { className: "text-left text-sm font-medium text-gray-900", children: llm.llm_name })] }, llm.llm_name));
264554
265133
  }) })] }) }));
264555
265134
  }
264556
265135
 
@@ -264575,6 +265154,10 @@ function AgentLLMTab({ showConfigurationHeader = true, getLLMProviderDetails, la
264575
265154
  }, {
264576
265155
  skip: !tenantKey || !username,
264577
265156
  });
265157
+ // Two alphabetical groups: providers we have a usable LLM key for first, then
265158
+ // the ones we don't. Ordering is independent of the search filter below, so it
265159
+ // holds for whatever the filter leaves.
265160
+ const sortedLLMProviders = React__default.useMemo(() => sortLLMProvidersByCredentials(llmProviders !== null && llmProviders !== void 0 ? llmProviders : [], getLLMProviderDetails), [llmProviders, getLLMProviderDetails]);
264578
265161
  const [editMentor, { isLoading: isEditingMentor }] = useEditMentorMutation();
264579
265162
  const isDisabled = isMentorSettingsLoading || isLoadingLLMProviders || isEditingMentor;
264580
265163
  const isLoading = isMentorSettingsLoading || isLoadingLLMProviders;
@@ -264604,10 +265187,21 @@ function AgentLLMTab({ showConfigurationHeader = true, getLLMProviderDetails, la
264604
265187
  overflowX: 'hidden',
264605
265188
  }, children: jsxs("div", { className: "space-y-6", children: [jsxs("div", { className: "relative", children: [jsx(Search, { className: "absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-gray-500" }), jsx(Input, { type: "search", placeholder: labels.search.placeholder, className: "w-full py-6 pl-9 md:w-1/2", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), disabled: isDisabled })] }), isLoading ? (jsx("div", { className: "flex items-center justify-center py-10", children: jsx(Spinner, {}) })) : (jsx(WithFormPermissions, { name: "llm_provider",
264606
265189
  // @ts-ignore
264607
- permissions: (_a = mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.permissions) === null || _a === void 0 ? void 0 : _a.field, enableRBAC: enableRBAC, children: ({ disabled }) => (jsx("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4", children: llmProviders === null || llmProviders === void 0 ? void 0 : llmProviders.filter((model) => model.name.toLowerCase().includes(searchQuery.toLowerCase())).map((model) => {
265190
+ permissions: (_a = mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.permissions) === null || _a === void 0 ? void 0 : _a.field, enableRBAC: enableRBAC, children: ({ disabled }) => (jsx("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4", children: sortedLLMProviders
265191
+ .filter((model) => model.name.toLowerCase().includes(searchQuery.toLowerCase()))
265192
+ .map((model) => {
264608
265193
  const providerDetails = getLLMProviderDetails(model.name);
264609
- return (jsxs("div", { className: cn('flex cursor-pointer items-center gap-3 rounded-lg border border-gray-200 bg-white p-4 shadow-sm transition-shadow hover:shadow-md', {
264610
- 'border-blue-500': (mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.llm_provider) === model.name,
265194
+ const isActive = (mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.llm_provider) === model.name;
265195
+ // The generated LLM response type doesn't declare the
265196
+ // credential flags the backend actually sends, so read them
265197
+ // through the access-shaped view the helper expects.
265198
+ const isGrayed = !canAccessProvider(model) && !isActive;
265199
+ return (jsxs("div", { "data-testid": "llm-provider-card", "data-provider": model.name, "data-disabled": isGrayed ? 'true' : 'false', className: cn('flex cursor-pointer items-center gap-3 rounded-lg border border-gray-200 bg-white p-4 shadow-sm transition-shadow hover:shadow-md', {
265200
+ 'border-blue-500': isActive,
265201
+ // Tint the whole card too: desaturating a logo that
265202
+ // is already black changes nothing, so the card
265203
+ // itself has to carry some of the inactive signal.
265204
+ 'border-gray-100 bg-gray-50': isGrayed,
264611
265205
  }), onClick: () => {
264612
265206
  if (isDisabled || disabled)
264613
265207
  return;
@@ -264624,7 +265218,12 @@ function AgentLLMTab({ showConfigurationHeader = true, getLLMProviderDetails, la
264624
265218
  // @ts-ignore - can_use_main_keys property does not exist on LLMResponse type
264625
265219
  can_use_main_keys: model === null || model === void 0 ? void 0 : model.can_use_main_keys,
264626
265220
  });
264627
- }, children: [jsx("div", { className: "h-8 w-8 flex-shrink-0", children: jsx(Image$4, { src: providerDetails.logo, alt: t('tabsLlmTab.providerLogoAlt', { name: providerDetails.name }), className: "h-full w-full object-contain", width: 32, height: 32, loading: "lazy" }) }), jsx("span", { className: "text-sm font-medium text-gray-900", children: providerDetails.name })] }, model.id));
265221
+ }, children: [jsx("div", { className: "h-8 w-8 flex-shrink-0", children: jsx(Image$4, { src: providerDetails.logo, alt: t('tabsLlmTab.providerLogoAlt', { name: providerDetails.name }), className: cn('h-full w-full object-contain', {
265222
+ // `grayscale` only desaturates, so already-black
265223
+ // marks (OpenAI, xAI) looked untouched. Fading is
265224
+ // what actually reads as inactive at any hue.
265225
+ 'opacity-40 grayscale': isGrayed,
265226
+ }), width: 32, height: 32, loading: "lazy" }) }), jsx("span", { className: "text-sm font-medium text-gray-900", children: providerDetails.name })] }, model.id));
264628
265227
  }) })) })), selectedLLMProvider && mentorSettings && (jsx(LLMProviderModal, { isOpen: true, onClose: () => setSelectedLLMProvider(null), onSelect: updateMentorLLM, llmProvider: selectedLLMProvider, isSelecting: isEditingMentor, mentorSettings: {
264629
265228
  llm_name: mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.llm_name,
264630
265229
  llm_provider: mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.llm_provider,
@@ -264750,7 +265349,7 @@ const BulkDeleteMemoryModal$1 = dynamic(() => Promise.resolve().then(function ()
264750
265349
  const ManageCategoriesModal$1 = dynamic(() => Promise.resolve().then(function () { return manageCategoriesModal; }).then((module) => ({
264751
265350
  default: module.ManageCategoriesModal,
264752
265351
  })), { ssr: false });
264753
- const PAGE_SIZE$2 = 20;
265352
+ const PAGE_SIZE$3 = 20;
264754
265353
  const SNAPSHOT_PAGE_SIZE = 1000;
264755
265354
  function ManageMemories({ tenantKey, username, mentorId }) {
264756
265355
  var _a, _b, _c, _d, _e;
@@ -264765,7 +265364,7 @@ function ManageMemories({ tenantKey, username, mentorId }) {
264765
265364
  setPage(1);
264766
265365
  }, [selectedLearner, dateRange, selectedCategorySlug]);
264767
265366
  const listParams = useMemo(() => {
264768
- const params = { page, page_size: PAGE_SIZE$2 };
265367
+ const params = { page, page_size: PAGE_SIZE$3 };
264769
265368
  if (selectedLearner)
264770
265369
  params.email = selectedLearner;
264771
265370
  if (selectedCategorySlug !== 'all')
@@ -264784,7 +265383,7 @@ function ManageMemories({ tenantKey, username, mentorId }) {
264784
265383
  }, {
264785
265384
  skip: !tenantKey || !username || !mentorId,
264786
265385
  });
264787
- const totalPages = Math.max(1, Math.ceil(((_a = listResponse === null || listResponse === void 0 ? void 0 : listResponse.count) !== null && _a !== void 0 ? _a : 0) / PAGE_SIZE$2));
265386
+ const totalPages = Math.max(1, Math.ceil(((_a = listResponse === null || listResponse === void 0 ? void 0 : listResponse.count) !== null && _a !== void 0 ? _a : 0) / PAGE_SIZE$3));
264788
265387
  const { data: adminCategories } = useGetMemoryCategoriesAdminQuery({
264789
265388
  org: tenantKey,
264790
265389
  mentorId,
@@ -265070,6 +265669,7 @@ const ANALYTICS_HUB_ICONS = {
265070
265669
  users: Users,
265071
265670
  topics: MessageSquare,
265072
265671
  transcripts: FileText,
265672
+ memory: Brain,
265073
265673
  financial: DollarSign,
265074
265674
  audit: ScrollText,
265075
265675
  reports: ChartColumn,
@@ -265084,6 +265684,7 @@ const ANALYTICS_HUB_ORDER = [
265084
265684
  { value: 'users', key: 'users' },
265085
265685
  { value: 'topics', key: 'topics' },
265086
265686
  { value: 'transcripts', key: 'transcripts' },
265687
+ { value: 'memory', key: 'memory' },
265087
265688
  { value: 'financial', key: 'financial' },
265088
265689
  { value: 'audit', key: 'audit' },
265089
265690
  { value: 'reports', key: 'reports' },
@@ -265119,6 +265720,10 @@ const AGENT_ANALYTICS_TAB_LABELS = {
265119
265720
  label: 'Transcripts',
265120
265721
  description: 'Read full conversation transcripts message by message.',
265121
265722
  },
265723
+ memory: {
265724
+ label: 'Memory',
265725
+ description: 'What this agent remembers about each user it talks to.',
265726
+ },
265122
265727
  financial: {
265123
265728
  label: 'Costs',
265124
265729
  description: 'Track token usage and what this agent is costing you.',
@@ -267708,7 +268313,7 @@ function toTitleCase(name) {
267708
268313
  .join(' ');
267709
268314
  }
267710
268315
 
267711
- const PAGE_SIZE$1 = 20;
268316
+ const PAGE_SIZE$2 = 20;
267712
268317
  function VoicePicker({ org, userId, provider, selectedVoiceId, onSelect, disabled, labels, }) {
267713
268318
  var _a, _b;
267714
268319
  const t = useT();
@@ -267728,13 +268333,13 @@ function VoicePicker({ org, userId, provider, selectedVoiceId, onSelect, disable
267728
268333
  userId,
267729
268334
  provider,
267730
268335
  page,
267731
- pageSize: PAGE_SIZE$1,
268336
+ pageSize: PAGE_SIZE$2,
267732
268337
  ...(debouncedSearch ? { search: debouncedSearch } : {}),
267733
268338
  }, { skip: !org || !userId });
267734
268339
  const paginated = data;
267735
268340
  const voices = (_a = paginated === null || paginated === void 0 ? void 0 : paginated.results) !== null && _a !== void 0 ? _a : [];
267736
268341
  const total = (_b = paginated === null || paginated === void 0 ? void 0 : paginated.count) !== null && _b !== void 0 ? _b : 0;
267737
- const totalPages = total > 0 ? Math.ceil(total / PAGE_SIZE$1) : 1;
268342
+ const totalPages = total > 0 ? Math.ceil(total / PAGE_SIZE$2) : 1;
267738
268343
  // Whenever a voice id is saved on the mentor settings, fetch it directly
267739
268344
  // so we can pin it at the top of the list. Otherwise the user can land
267740
268345
  // on a page that doesn't contain the selected voice and the selection
@@ -268626,11 +269231,12 @@ const AGENT_GRADER_TAB_LABELS = {
268626
269231
  offHint: 'Turn it on to write grading instructions and build your rubric. Anything you set up here is kept safe if you later turn it off.',
268627
269232
  },
268628
269233
  subTabs: {
268629
- setup: 'Grading setup',
269234
+ setup: 'Grading Setup',
268630
269235
  rubric: 'Rubric',
269236
+ results: 'Results',
268631
269237
  },
268632
269238
  denied: {
268633
- title: 'No access to grading settings',
269239
+ title: 'No Access to Grading Settings',
268634
269240
  description: "You don't have permission to view this agent's grading setup. Ask an administrator for access.",
268635
269241
  },
268636
269242
  warnings: {
@@ -268638,12 +269244,12 @@ const AGENT_GRADER_TAB_LABELS = {
268638
269244
  noCriteria: 'Grading is on but the rubric is empty — the agent will fail when asked to grade. Add at least one criterion in the Rubric tab.',
268639
269245
  },
268640
269246
  config: {
268641
- sectionTitle: 'Grading setup',
269247
+ sectionTitle: 'Grading Setup',
268642
269248
  gradingMode: {
268643
- label: 'What gets graded',
269249
+ label: 'What Gets Graded',
268644
269250
  options: {
268645
- submission: 'A submission',
268646
- conversation: 'The conversation',
269251
+ submission: 'A Submission',
269252
+ conversation: 'The Conversation',
268647
269253
  },
268648
269254
  help: {
268649
269255
  submission: 'The agent grades a piece of work the person hands in, like an essay or answer.',
@@ -268651,16 +269257,16 @@ const AGENT_GRADER_TAB_LABELS = {
268651
269257
  },
268652
269258
  },
268653
269259
  feedbackMode: {
268654
- label: 'Feedback shared with the person',
269260
+ label: 'Feedback Shared with the Person',
268655
269261
  options: {
268656
- overall: 'Overall feedback only',
268657
- perCriteria: 'Feedback per criterion',
268658
- both: 'Overall + per criterion',
269262
+ overall: 'Overall Feedback Only',
269263
+ perCriteria: 'Feedback per Criterion',
269264
+ both: 'Overall + per Criterion',
268659
269265
  },
268660
269266
  help: 'The overall score is always calculated — this only controls how much detail is shared back.',
268661
269267
  },
268662
269268
  instructions: {
268663
- label: 'Grading instructions',
269269
+ label: 'Grading Instructions',
268664
269270
  placeholder: "Describe what's being graded and anything the grader should know beyond the rubric. E.g. 'You are grading a 500-word persuasive essay on climate policy written by a high-school student. Be encouraging but honest.'",
268665
269271
  help: 'Required. This is the context the agent uses every time it grades.',
268666
269272
  },
@@ -268672,7 +269278,7 @@ const AGENT_GRADER_TAB_LABELS = {
268672
269278
  description: 'Each row is one thing the agent looks for and how many points it is worth. The overall score is points earned out of the total.',
268673
269279
  needsConfigHint: 'Save your grading setup first, then add rubric criteria here.',
268674
269280
  emptyState: 'No criteria yet. Add your first one — for example "Clarity", 10 points.',
268675
- addButton: 'Add criterion',
269281
+ addButton: 'Add Criterion',
268676
269282
  columns: {
268677
269283
  name: 'Name',
268678
269284
  criteria: 'Criteria',
@@ -268681,12 +269287,12 @@ const AGENT_GRADER_TAB_LABELS = {
268681
269287
  actionsAria: (name) => `Actions for ${name}`,
268682
269288
  edit: 'Edit',
268683
269289
  delete: 'Delete',
268684
- totalPoints: 'Total possible points: {total}',
269290
+ totalPoints: 'Total Possible Points: {total}',
268685
269291
  scoreHint: 'Overall score = points earned ÷ {total}.',
268686
269292
  lastCriterionHint: 'A rubric needs at least one criterion — add another before removing this one.',
268687
269293
  modal: {
268688
- addTitle: 'Add criterion',
268689
- editTitle: 'Edit criterion',
269294
+ addTitle: 'Add Criterion',
269295
+ editTitle: 'Edit Criterion',
268690
269296
  name: {
268691
269297
  label: 'Name',
268692
269298
  placeholder: 'e.g. Clarity',
@@ -268708,7 +269314,7 @@ const AGENT_GRADER_TAB_LABELS = {
268708
269314
  cancel: 'Cancel',
268709
269315
  },
268710
269316
  deleteModal: {
268711
- title: 'Delete criterion',
269317
+ title: 'Delete Criterion',
268712
269318
  confirmationPrefix: 'Are you sure you want to remove',
268713
269319
  confirmationSuffix: ' from the rubric?',
268714
269320
  cancel: 'Cancel',
@@ -268716,6 +269322,59 @@ const AGENT_GRADER_TAB_LABELS = {
268716
269322
  deleting: 'Deleting…',
268717
269323
  },
268718
269324
  },
269325
+ results: {
269326
+ sectionTitle: 'Grade Results',
269327
+ description: 'Every grade this agent has issued. Override a score to correct it — the change is pushed back to the LMS the grade came from.',
269328
+ filters: {
269329
+ searchUser: 'Search for User',
269330
+ searchUsersPlaceholder: 'Search users...',
269331
+ noUsersFound: 'No users found.',
269332
+ allUsers: 'All Users',
269333
+ statusLabel: 'Score Status',
269334
+ dateRangeLabel: 'Pick a Date Range',
269335
+ allStatuses: 'All Statuses',
269336
+ },
269337
+ statusValues: {
269338
+ pending: 'Pending',
269339
+ published: 'Published',
269340
+ failed: 'Failed',
269341
+ },
269342
+ columns: {
269343
+ learner: 'Learner',
269344
+ score: 'Score',
269345
+ status: 'Status',
269346
+ override: 'Override',
269347
+ gradedAt: 'Graded',
269348
+ },
269349
+ emptyState: 'No grades yet. They will appear here once the agent starts grading.',
269350
+ emptyFiltered: 'No grades match the current filters.',
269351
+ overriddenBadge: 'Overridden',
269352
+ overrideButton: 'Override',
269353
+ overrideButtonAria: (email) => `Override grade for ${email}`,
269354
+ overrideModal: {
269355
+ title: 'Override Grade',
269356
+ learnerLabel: 'Learner',
269357
+ aiScoreLabel: 'AI Score',
269358
+ currentOverrideLabel: 'Current Override',
269359
+ noOverride: 'None',
269360
+ pointsLabel: 'Override Points',
269361
+ pointsHelp: 'Between 0 and {total}. The score pushed to the LMS becomes points ÷ {total}.',
269362
+ pointsRange: 'Points must be between 0 and {total}',
269363
+ feedbackLabel: 'Override Feedback',
269364
+ feedbackPlaceholder: 'Explain the change for the learner (optional).',
269365
+ clearHelp: 'Clearing the override restores the AI score and pushes it back to the LMS.',
269366
+ save: 'Save',
269367
+ saving: 'Saving…',
269368
+ clear: 'Clear',
269369
+ clearing: 'Clearing…',
269370
+ cancel: 'Cancel',
269371
+ },
269372
+ toasts: {
269373
+ overrideSaved: 'Override saved and pushed to the LMS',
269374
+ overrideCleared: 'Override cleared — the AI score was restored',
269375
+ overrideError: "Couldn't save the override",
269376
+ },
269377
+ },
268719
269378
  toasts: {
268720
269379
  toggleOn: 'Grading turned on',
268721
269380
  toggleOff: 'Grading turned off — your rubric is kept for next time',
@@ -268742,6 +269401,7 @@ function buildGraderTabLabels(t) {
268742
269401
  subTabs: {
268743
269402
  setup: t('graderTabLabels.subTabs.setup'),
268744
269403
  rubric: t('graderTabLabels.subTabs.rubric'),
269404
+ results: t('graderTabLabels.subTabs.results'),
268745
269405
  },
268746
269406
  denied: {
268747
269407
  title: t('graderTabLabels.denied.title'),
@@ -268829,6 +269489,59 @@ function buildGraderTabLabels(t) {
268829
269489
  deleting: t('graderTabLabels.criteria.deleteModal.deleting'),
268830
269490
  },
268831
269491
  },
269492
+ results: {
269493
+ sectionTitle: t('graderTabLabels.results.sectionTitle'),
269494
+ description: t('graderTabLabels.results.description'),
269495
+ filters: {
269496
+ searchUser: t('graderTabLabels.results.filters.searchUser'),
269497
+ searchUsersPlaceholder: t('graderTabLabels.results.filters.searchUsersPlaceholder'),
269498
+ noUsersFound: t('graderTabLabels.results.filters.noUsersFound'),
269499
+ allUsers: t('graderTabLabels.results.filters.allUsers'),
269500
+ statusLabel: t('graderTabLabels.results.filters.statusLabel'),
269501
+ dateRangeLabel: t('graderTabLabels.results.filters.dateRangeLabel'),
269502
+ allStatuses: t('graderTabLabels.results.filters.allStatuses'),
269503
+ },
269504
+ statusValues: {
269505
+ pending: t('graderTabLabels.results.statusValues.pending'),
269506
+ published: t('graderTabLabels.results.statusValues.published'),
269507
+ failed: t('graderTabLabels.results.statusValues.failed'),
269508
+ },
269509
+ columns: {
269510
+ learner: t('graderTabLabels.results.columns.learner'),
269511
+ score: t('graderTabLabels.results.columns.score'),
269512
+ status: t('graderTabLabels.results.columns.status'),
269513
+ override: t('graderTabLabels.results.columns.override'),
269514
+ gradedAt: t('graderTabLabels.results.columns.gradedAt'),
269515
+ },
269516
+ emptyState: t('graderTabLabels.results.emptyState'),
269517
+ emptyFiltered: t('graderTabLabels.results.emptyFiltered'),
269518
+ overriddenBadge: t('graderTabLabels.results.overriddenBadge'),
269519
+ overrideButton: t('graderTabLabels.results.overrideButton'),
269520
+ overrideButtonAria: (email) => t('graderTabLabels.results.overrideButtonAria', { email }),
269521
+ overrideModal: {
269522
+ title: t('graderTabLabels.results.overrideModal.title'),
269523
+ learnerLabel: t('graderTabLabels.results.overrideModal.learnerLabel'),
269524
+ aiScoreLabel: t('graderTabLabels.results.overrideModal.aiScoreLabel'),
269525
+ currentOverrideLabel: t('graderTabLabels.results.overrideModal.currentOverrideLabel'),
269526
+ noOverride: t('graderTabLabels.results.overrideModal.noOverride'),
269527
+ pointsLabel: t('graderTabLabels.results.overrideModal.pointsLabel'),
269528
+ pointsHelp: t('graderTabLabels.results.overrideModal.pointsHelp'),
269529
+ pointsRange: t('graderTabLabels.results.overrideModal.pointsRange'),
269530
+ feedbackLabel: t('graderTabLabels.results.overrideModal.feedbackLabel'),
269531
+ feedbackPlaceholder: t('graderTabLabels.results.overrideModal.feedbackPlaceholder'),
269532
+ clearHelp: t('graderTabLabels.results.overrideModal.clearHelp'),
269533
+ save: t('graderTabLabels.results.overrideModal.save'),
269534
+ saving: t('graderTabLabels.results.overrideModal.saving'),
269535
+ clear: t('graderTabLabels.results.overrideModal.clear'),
269536
+ clearing: t('graderTabLabels.results.overrideModal.clearing'),
269537
+ cancel: t('graderTabLabels.results.overrideModal.cancel'),
269538
+ },
269539
+ toasts: {
269540
+ overrideSaved: t('graderTabLabels.results.toasts.overrideSaved'),
269541
+ overrideCleared: t('graderTabLabels.results.toasts.overrideCleared'),
269542
+ overrideError: t('graderTabLabels.results.toasts.overrideError'),
269543
+ },
269544
+ },
268832
269545
  toasts: {
268833
269546
  toggleOn: t('graderTabLabels.toasts.toggleOn'),
268834
269547
  toggleOff: t('graderTabLabels.toasts.toggleOff'),
@@ -268849,6 +269562,141 @@ function resolveGraderTabLabels(override, t) {
268849
269562
  return deepMerge(base, override);
268850
269563
  }
268851
269564
 
269565
+ function formatPercent$1(score) {
269566
+ return `${Math.round(score * 100)}%`;
269567
+ }
269568
+ function errorDetail$1(error) {
269569
+ var _a;
269570
+ const detail = (_a = error === null || error === void 0 ? void 0 : error.data) === null || _a === void 0 ? void 0 : _a.detail;
269571
+ return typeof detail === 'string' ? detail : '';
269572
+ }
269573
+ /**
269574
+ * Popup that records a human override for one grade result and pushes it to
269575
+ * the LMS the grade came from. Points are in rubric points (0 to the
269576
+ * rubric's total); clearing restores the AI score.
269577
+ */
269578
+ function GradeResultOverrideModal({ isOpen, onClose, result, tenantKey, mentorId, labels, }) {
269579
+ var _a, _b;
269580
+ const m = labels.overrideModal;
269581
+ // The list row doesn't carry the rubric total or existing override text —
269582
+ // fetch the full record while the modal is open.
269583
+ const { data: detail, isLoading } = useGetGradeResultQuery({ org: tenantKey, mentorId, resultId: (_a = result === null || result === void 0 ? void 0 : result.id) !== null && _a !== void 0 ? _a : '' }, { skip: !isOpen || !result });
269584
+ const [overrideGradeResult, { isLoading: isSaving }] = useOverrideGradeResultMutation();
269585
+ const [points, setPoints] = React__default.useState('');
269586
+ const [feedback, setFeedback] = React__default.useState('');
269587
+ const [showErrors, setShowErrors] = React__default.useState(false);
269588
+ const [isClearing, setIsClearing] = React__default.useState(false);
269589
+ // Prefill from the fetched record each time the modal opens.
269590
+ React__default.useEffect(() => {
269591
+ var _a;
269592
+ if (!isOpen)
269593
+ return;
269594
+ setShowErrors(false);
269595
+ setPoints((detail === null || detail === void 0 ? void 0 : detail.override_points) != null ? String(detail.override_points) : '');
269596
+ setFeedback((_a = detail === null || detail === void 0 ? void 0 : detail.override_feedback) !== null && _a !== void 0 ? _a : '');
269597
+ }, [isOpen, detail === null || detail === void 0 ? void 0 : detail.id, detail === null || detail === void 0 ? void 0 : detail.override_points, detail === null || detail === void 0 ? void 0 : detail.override_feedback]);
269598
+ const totalPoints = (_b = detail === null || detail === void 0 ? void 0 : detail.total_points) !== null && _b !== void 0 ? _b : 0;
269599
+ // Hard UI cap: overrides are entered as 0–100, additionally bounded by the
269600
+ // rubric's own total when it is smaller.
269601
+ const maxPoints = totalPoints > 0 ? Math.min(totalPoints, 100) : 100;
269602
+ const parsedPoints = Number.parseFloat(points);
269603
+ const pointsValid = Number.isFinite(parsedPoints) && parsedPoints >= 0 && parsedPoints <= maxPoints;
269604
+ const hasExistingOverride = (detail === null || detail === void 0 ? void 0 : detail.override_points) != null;
269605
+ const submit = async (formData) => {
269606
+ if (!result)
269607
+ return false;
269608
+ try {
269609
+ await overrideGradeResult({
269610
+ org: tenantKey,
269611
+ mentorId,
269612
+ resultId: result.id,
269613
+ formData,
269614
+ }).unwrap();
269615
+ return true;
269616
+ }
269617
+ catch (error) {
269618
+ console.error('[GradeResultOverrideModal] override error', error);
269619
+ toast.error(errorDetail$1(error) || labels.toasts.overrideError);
269620
+ return false;
269621
+ }
269622
+ };
269623
+ const handleSave = async () => {
269624
+ if (!pointsValid) {
269625
+ setShowErrors(true);
269626
+ return;
269627
+ }
269628
+ const saved = await submit({ override_points: parsedPoints, override_feedback: feedback });
269629
+ if (saved) {
269630
+ toast.success(labels.toasts.overrideSaved);
269631
+ onClose();
269632
+ }
269633
+ };
269634
+ const handleClear = async () => {
269635
+ setIsClearing(true);
269636
+ const cleared = await submit({ override_points: null });
269637
+ setIsClearing(false);
269638
+ if (cleared) {
269639
+ toast.success(labels.toasts.overrideCleared);
269640
+ onClose();
269641
+ }
269642
+ };
269643
+ return (jsx(Dialog, { open: isOpen, onOpenChange: onClose, children: jsxs(DialogContent, { "aria-describedby": "grader-override-modal-description", "data-testid": "grader-override-modal", children: [jsx(DialogHeader, { children: jsx(DialogTitle, { className: "ibl-dialog-title", children: m.title }) }), isLoading || !detail ? (jsx("div", { className: "flex items-center justify-center py-8", "data-testid": "grader-override-loading", children: jsx(Spinner, {}) })) : (jsxs("div", { className: "space-y-4", id: "grader-override-modal-description", children: [jsxs("div", { className: "grid grid-cols-2 gap-2 rounded-lg border border-gray-200 bg-gray-50/60 p-3 text-sm", children: [jsx("span", { className: "text-gray-500", children: m.learnerLabel }), jsx("span", { className: "truncate text-gray-900", title: detail.student.email, children: detail.student.email }), jsx("span", { className: "text-gray-500", children: m.aiScoreLabel }), jsx("span", { className: "text-gray-900", children: formatPercent$1(detail.score) }), jsx("span", { className: "text-gray-500", children: m.currentOverrideLabel }), jsx("span", { className: "text-gray-900", "data-testid": "grader-override-current", children: hasExistingOverride ? `${detail.override_points} / ${totalPoints}` : m.noOverride })] }), jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { htmlFor: "grader-override-points", children: m.pointsLabel }), jsx(Input, { id: "grader-override-points", type: "number", min: 0, max: maxPoints, step: "any", value: points, className: "w-28", onChange: (e) => setPoints(e.target.value) }), jsx("p", { className: "text-xs text-gray-500", children: m.pointsHelp.replace('{total}', String(maxPoints)) }), showErrors && !pointsValid && (jsx("p", { className: "text-xs text-red-500", children: m.pointsRange.replace('{total}', String(maxPoints)) }))] }), jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { htmlFor: "grader-override-feedback", children: m.feedbackLabel }), jsx(Textarea, { id: "grader-override-feedback", value: feedback, placeholder: m.feedbackPlaceholder, rows: 3, onChange: (e) => setFeedback(e.target.value) })] }), hasExistingOverride && jsx("p", { className: "text-xs text-gray-500", children: m.clearHelp })] })), jsxs(DialogFooter, { className: "gap-3", children: [hasExistingOverride && (jsx(Button$1, { variant: "outline", className: "mr-auto text-red-600 hover:text-red-700", onClick: handleClear, disabled: isSaving || isLoading, "data-testid": "grader-override-clear", children: isClearing ? m.clearing : m.clear })), jsx(Button$1, { variant: "outline", onClick: onClose, children: m.cancel }), jsx(Button$1, { className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", onClick: handleSave, disabled: isSaving || isLoading, "data-testid": "grader-override-save", children: isSaving && !isClearing ? m.saving : m.save })] })] }) }));
269644
+ }
269645
+
269646
+ const PAGE_SIZE$1 = 10;
269647
+ const STATUS_ALL = 'all';
269648
+ function formatPercent(score) {
269649
+ return `${Math.round(score * 100)}%`;
269650
+ }
269651
+ function ResultsSection({ labels, tenantKey, mentorId, username, canOverride, }) {
269652
+ var _a, _b, _c;
269653
+ const [selectedEmail, setSelectedEmail] = React__default.useState('');
269654
+ const [status, setStatus] = React__default.useState(STATUS_ALL);
269655
+ const [dateRange, setDateRange] = React__default.useState(undefined);
269656
+ const [page, setPage] = React__default.useState(1);
269657
+ const [resultToOverride, setResultToOverride] = React__default.useState(null);
269658
+ const gradedAtAfter = (dateRange === null || dateRange === void 0 ? void 0 : dateRange.from)
269659
+ ? `${format$1(dateRange.from, 'yyyy-MM-dd')}T00:00:00Z`
269660
+ : undefined;
269661
+ const gradedAtBefore = (dateRange === null || dateRange === void 0 ? void 0 : dateRange.to)
269662
+ ? `${format$1(dateRange.to, 'yyyy-MM-dd')}T23:59:59Z`
269663
+ : undefined;
269664
+ const hasFilters = Boolean(selectedEmail) ||
269665
+ status !== STATUS_ALL ||
269666
+ Boolean(gradedAtAfter) ||
269667
+ Boolean(gradedAtBefore);
269668
+ // Any filter change restarts from the first page.
269669
+ React__default.useEffect(() => {
269670
+ setPage(1);
269671
+ }, [selectedEmail, status, gradedAtAfter, gradedAtBefore]);
269672
+ // Same learner list the History tab's "Search for User" control uses.
269673
+ const { data: chatHistoryFilter } = useGetChatHistoryFilterQuery({
269674
+ org: tenantKey,
269675
+ // @ts-expect-error userId is accepted by the API but not typed
269676
+ userId: username || '',
269677
+ mentorId,
269678
+ });
269679
+ const learners = (_a = chatHistoryFilter === null || chatHistoryFilter === void 0 ? void 0 : chatHistoryFilter.users) !== null && _a !== void 0 ? _a : [];
269680
+ const { data, isLoading, isFetching } = useListGradeResultsQuery({
269681
+ org: tenantKey,
269682
+ mentorId,
269683
+ email: selectedEmail || undefined,
269684
+ status: status === STATUS_ALL ? undefined : status,
269685
+ graded_at_after: gradedAtAfter,
269686
+ graded_at_before: gradedAtBefore,
269687
+ page,
269688
+ page_size: PAGE_SIZE$1,
269689
+ }, { skip: !tenantKey || !mentorId });
269690
+ const results = (_b = data === null || data === void 0 ? void 0 : data.results) !== null && _b !== void 0 ? _b : [];
269691
+ const totalPages = Math.max(1, Math.ceil(((_c = data === null || data === void 0 ? void 0 : data.count) !== null && _c !== void 0 ? _c : 0) / PAGE_SIZE$1));
269692
+ const statusText = (value) => value ? labels.statusValues[value] : '—';
269693
+ return (jsxs("div", { className: "space-y-3", "data-testid": "grader-results-section", children: [jsxs("div", { children: [jsx("h4", { className: "text-sm font-medium text-gray-900", children: labels.sectionTitle }), jsx("p", { className: "text-xs text-gray-600", children: labels.description })] }), jsx("div", { className: "rounded-lg border p-4", children: jsxs("div", { className: "flex flex-col gap-3 lg:flex-row", children: [jsx("div", { className: "relative min-w-[200px] flex-1", children: jsxs(Popover, { children: [jsx(PopoverTrigger, { asChild: true, children: jsxs(Button$1, { variant: "outline", role: "combobox", "aria-label": labels.filters.searchUser, className: "w-full justify-between bg-transparent font-normal", "data-testid": "grader-results-user-filter", children: [selectedEmail || labels.filters.searchUser, jsx(ChevronsUpDown, { className: "ml-2 h-4 w-4 shrink-0 opacity-50" })] }) }), jsx(PopoverContent, { className: "w-full p-0", align: "start", portalled: false, children: jsxs(Command, { children: [jsx(CommandInput, { placeholder: labels.filters.searchUsersPlaceholder }), jsxs(CommandList, { children: [jsx(CommandEmpty, { children: labels.filters.noUsersFound }), jsxs(CommandGroup, { children: [jsxs(CommandItem, { value: "", onSelect: () => setSelectedEmail(''), children: [jsx(Check, { className: cn('mr-2 h-4 w-4', !selectedEmail ? 'opacity-100' : 'opacity-0') }), labels.filters.allUsers] }), learners.map((learner) => (jsxs(CommandItem, { value: learner.email, onSelect: () => setSelectedEmail(learner.email), children: [jsx(Check, { className: cn('mr-2 h-4 w-4', selectedEmail === learner.email ? 'opacity-100' : 'opacity-0') }), jsx("div", { className: "flex flex-col", children: jsx("span", { className: "font-medium text-gray-700", children: learner.email }) })] }, learner.email)))] })] })] }) })] }) }), jsxs(Select$1, { value: status, onValueChange: setStatus, children: [jsx(SelectTrigger, { className: "w-full lg:w-[160px]", "aria-label": labels.filters.statusLabel, "data-testid": "grader-results-status-filter", children: jsx(SelectValue, { placeholder: labels.filters.statusLabel }) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: STATUS_ALL, children: labels.filters.allStatuses }), jsx(SelectItem, { value: "pending", children: labels.statusValues.pending }), jsx(SelectItem, { value: "published", children: labels.statusValues.published }), jsx(SelectItem, { value: "failed", children: labels.statusValues.failed })] })] }), jsxs(Popover, { children: [jsx(PopoverTrigger, { asChild: true, children: jsxs(Button$1, { variant: "outline", className: "flex w-full items-center gap-2 bg-transparent font-normal whitespace-nowrap lg:w-auto", "data-testid": "grader-results-date-filter", children: [jsx(Calendar$1, { className: "h-4 w-4" }), (dateRange === null || dateRange === void 0 ? void 0 : dateRange.from) && (dateRange === null || dateRange === void 0 ? void 0 : dateRange.to)
269694
+ ? `${format$1(dateRange.from, 'MMM dd')} - ${format$1(dateRange.to, 'MMM dd')}`
269695
+ : labels.filters.dateRangeLabel] }) }), jsx(PopoverContent, { className: "w-auto p-0", align: "start", children: jsx(Calendar, { mode: "range", selected: dateRange, onSelect: setDateRange, numberOfMonths: 2, initialFocus: true }) })] })] }) }), jsx("div", { className: "overflow-hidden rounded-md border", children: jsx("div", { className: "overflow-x-auto sm:mx-0", children: jsx("div", { className: "inline-block min-w-full align-middle", children: isLoading ? (jsx("div", { className: "flex w-full items-center justify-center py-10", children: jsx(Spinner, {}) })) : (jsxs(Table$1, { className: "min-w-full", children: [jsx(TableHeader$1, { children: jsxs(TableRow$1, { className: "bg-muted/50 border-b", children: [jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: labels.columns.learner }), jsx(TableHead, { className: "p-3 text-right text-sm whitespace-nowrap text-[#646464]", children: labels.columns.score }), jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: labels.columns.status }), jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: labels.columns.override }), jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: labels.columns.gradedAt }), jsx(TableHead, { className: "w-10 p-3" })] }) }), jsx(TableBody, { children: results.length === 0 ? (jsx(TableRow$1, { children: jsx(TableCell$1, { colSpan: 6, className: "p-4 text-center text-[#646464]", children: hasFilters ? labels.emptyFiltered : labels.emptyState }) })) : (results.map((result) => (jsxs(TableRow$1, { className: "border-b last:border-0", "data-testid": `grader-result-row-${result.id}`, children: [jsx(TableCell$1, { className: "max-w-[220px] truncate p-3 font-medium whitespace-nowrap text-[#646464]", title: result.student.email, children: result.student.email }), jsx(TableCell$1, { className: "p-3 text-right whitespace-nowrap text-[#646464]", children: formatPercent(result.effective_score) }), jsx(TableCell$1, { className: "p-3 whitespace-nowrap text-[#646464]", children: statusText(result.status) }), jsx(TableCell$1, { className: "min-w-[200px] max-w-[320px] p-3 text-[#646464]", children: result.override_score != null
269696
+ ? `${labels.overriddenBadge} · ${formatPercent(result.override_score)} · ${statusText(result.override_status)}`
269697
+ : '—' }), jsx(TableCell$1, { className: "p-3 whitespace-nowrap text-[#646464]", children: formatDistanceToNow(new Date(result.graded_at), { addSuffix: true }) }), jsx(TableCell$1, { className: "p-3 text-right", children: canOverride && (jsx(Button$1, { type: "button", variant: "outline", size: "sm", onClick: () => setResultToOverride(result), "aria-label": labels.overrideButtonAria(result.student.email), "data-testid": `grader-result-override-${result.id}`, children: labels.overrideButton })) })] }, result.id)))) })] })) }) }) }), totalPages > 1 && (jsx(IblPagination, { currentPage: page, totalPages: totalPages, onPageChange: setPage, disabled: isLoading || isFetching })), jsx(GradeResultOverrideModal, { isOpen: resultToOverride !== null, onClose: () => setResultToOverride(null), result: resultToOverride, tenantKey: tenantKey, mentorId: mentorId, labels: labels })] }));
269698
+ }
269699
+
268852
269700
  function CriterionDeleteModal({ isOpen, onClose, criterion, labels, isDeleting, onConfirm, }) {
268853
269701
  const handleDelete = async () => {
268854
269702
  if (!criterion)
@@ -268900,25 +269748,25 @@ function CriterionModal({ isOpen, onClose, criterion, labels, isSaving, onSave }
268900
269748
  return (jsx(Dialog, { open: isOpen, onOpenChange: onClose, children: jsxs(DialogContent, { "aria-describedby": "grader-criterion-modal-description", "data-testid": "grader-criterion-modal", children: [jsx(DialogHeader, { children: jsx(DialogTitle, { className: "ibl-dialog-title", children: isEditing ? labels.editTitle : labels.addTitle }) }), jsxs("div", { className: "space-y-4", id: "grader-criterion-modal-description", children: [jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { htmlFor: "grader-criterion-name", children: labels.name.label }), jsx(Input, { id: "grader-criterion-name", value: draft.name, placeholder: labels.name.placeholder, onChange: (e) => setDraft((prev) => ({ ...prev, name: e.target.value })) }), showErrors && errors.name && jsx("p", { className: "text-xs text-red-500", children: errors.name })] }), jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { htmlFor: "grader-criterion-criteria", children: labels.criteria.label }), jsx(Textarea, { id: "grader-criterion-criteria", value: draft.criteria, placeholder: labels.criteria.placeholder, rows: 3, onChange: (e) => setDraft((prev) => ({ ...prev, criteria: e.target.value })) }), showErrors && errors.criteria && (jsx("p", { className: "text-xs text-red-500", children: errors.criteria }))] }), jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { htmlFor: "grader-criterion-points", children: labels.points.label }), jsx(Input, { id: "grader-criterion-points", type: "number", min: 0, step: "any", value: draft.points, placeholder: labels.points.placeholder, className: "w-28", onChange: (e) => setDraft((prev) => ({ ...prev, points: e.target.value })) }), showErrors && errors.points && jsx("p", { className: "text-xs text-red-500", children: errors.points })] })] }), jsxs(DialogFooter, { className: "gap-3", children: [jsx(Button$1, { variant: "outline", onClick: onClose, children: labels.cancel }), jsx(Button$1, { className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", onClick: handleSubmit, disabled: isSaving, "data-testid": "grader-criterion-modal-save", children: isSaving ? labels.saving : labels.save })] })] }) }));
268901
269749
  }
268902
269750
 
268903
- function RubricSection({ labels, criteria, hasConfig, isLoading, isMutating, totalPoints, onAdd, onEdit, onDelete, }) {
269751
+ function RubricSection({ labels, criteria, hasConfig, isLoading, isMutating, totalPoints, canAdd, canEdit, canDelete, onAdd, onEdit, onDelete, }) {
268904
269752
  const [showCreate, setShowCreate] = React__default.useState(false);
268905
269753
  const [criterionToEdit, setCriterionToEdit] = React__default.useState(null);
268906
269754
  const [criterionToDelete, setCriterionToDelete] = React__default.useState(null);
268907
269755
  const isLastCriterion = criteria.length === 1;
268908
- return (jsxs("div", { className: "space-y-3", "data-testid": "grader-criteria-section", children: [jsxs("div", { className: "flex items-start justify-between gap-4", children: [jsxs("div", { children: [jsx("h4", { className: "text-sm font-medium text-gray-900", children: labels.sectionTitle }), jsx("p", { className: "text-xs text-gray-600", children: labels.description })] }), jsxs(Button$1, { className: "ibl-button-primary flex-shrink-0", onClick: () => setShowCreate(true), disabled: !hasConfig || isMutating, "data-testid": "grader-add-criterion-button", children: [jsx(Plus, { className: "mr-1 h-4 w-4" }), labels.addButton] })] }), jsx("div", { className: "overflow-hidden rounded-md border", children: jsx("div", { className: "overflow-x-auto sm:mx-0", children: jsx("div", { className: "inline-block min-w-full align-middle", children: isLoading ? (jsx("div", { className: "flex w-full items-center justify-center py-10", children: jsx(Spinner, {}) })) : (jsxs(Table$1, { className: "min-w-full", children: [jsx(TableHeader$1, { children: jsxs(TableRow$1, { className: "bg-muted/50 border-b", children: [jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: labels.columns.name }), jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: labels.columns.criteria }), jsx(TableHead, { className: "p-3 text-right text-sm whitespace-nowrap text-[#646464]", children: labels.columns.points }), jsx(TableHead, { className: "w-10 p-3" })] }) }), jsx(TableBody, { children: criteria.length === 0 ? (jsx(TableRow$1, { children: jsx(TableCell$1, { colSpan: 4, className: "p-4 text-center text-[#646464]", children: hasConfig ? labels.emptyState : labels.needsConfigHint }) })) : (criteria.map((criterion) => (jsxs(TableRow$1, { className: "border-b last:border-0", "data-testid": `grader-criterion-row-${criterion.id}`, children: [jsx(TableCell$1, { className: "max-w-[160px] truncate p-3 font-medium whitespace-nowrap text-[#646464]", children: criterion.name }), jsx(TableCell$1, { className: "max-w-[280px] truncate p-3 text-[#646464]", title: criterion.criteria, children: criterion.criteria }), jsx(TableCell$1, { className: "p-3 text-right whitespace-nowrap text-[#646464]", children: criterion.points }), jsx(TableCell$1, { className: "p-3 text-right", children: jsxs(DropdownMenu, { children: [jsx(DropdownMenuTrigger, { asChild: true, children: jsx(Button$1, { type: "button", variant: "ghost", size: "icon", "aria-label": labels.actionsAria(criterion.name), children: jsx(Ellipsis, { className: "h-4 w-4" }) }) }), jsxs(DropdownMenuContent, { align: "end", children: [jsxs(DropdownMenuItem, { onClick: () => setCriterionToEdit(criterion), children: [jsx(Pencil, { className: "mr-2 h-4 w-4" }), labels.edit] }), jsxs(DropdownMenuItem, { className: "text-red-600 focus:text-red-700", disabled: isLastCriterion, onClick: () => {
269756
+ return (jsxs("div", { className: "space-y-3", "data-testid": "grader-criteria-section", children: [jsxs("div", { className: "flex items-start justify-between gap-4", children: [jsxs("div", { children: [jsx("h4", { className: "text-sm font-medium text-gray-900", children: labels.sectionTitle }), jsx("p", { className: "text-xs text-gray-600", children: labels.description })] }), canAdd && (jsxs(Button$1, { className: "ibl-button-primary flex-shrink-0", onClick: () => setShowCreate(true), disabled: !hasConfig || isMutating, "data-testid": "grader-add-criterion-button", children: [jsx(Plus, { className: "mr-1 h-4 w-4" }), labels.addButton] }))] }), jsx("div", { className: "overflow-hidden rounded-md border", children: jsx("div", { className: "overflow-x-auto sm:mx-0", children: jsx("div", { className: "inline-block min-w-full align-middle", children: isLoading ? (jsx("div", { className: "flex w-full items-center justify-center py-10", children: jsx(Spinner, {}) })) : (jsxs(Table$1, { className: "min-w-full", children: [jsx(TableHeader$1, { children: jsxs(TableRow$1, { className: "bg-muted/50 border-b", children: [jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: labels.columns.name }), jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: labels.columns.criteria }), jsx(TableHead, { className: "p-3 text-right text-sm whitespace-nowrap text-[#646464]", children: labels.columns.points }), jsx(TableHead, { className: "w-10 p-3" })] }) }), jsx(TableBody, { children: criteria.length === 0 ? (jsx(TableRow$1, { children: jsx(TableCell$1, { colSpan: 4, className: "p-4 text-center text-[#646464]", children: hasConfig ? labels.emptyState : labels.needsConfigHint }) })) : (criteria.map((criterion) => (jsxs(TableRow$1, { className: "border-b last:border-0", "data-testid": `grader-criterion-row-${criterion.id}`, children: [jsx(TableCell$1, { className: "max-w-[160px] truncate p-3 font-medium whitespace-nowrap text-[#646464]", children: criterion.name }), jsx(TableCell$1, { className: "max-w-[280px] truncate p-3 text-[#646464]", title: criterion.criteria, children: criterion.criteria }), jsx(TableCell$1, { className: "p-3 text-right whitespace-nowrap text-[#646464]", children: criterion.points }), jsx(TableCell$1, { className: "p-3 text-right", children: (canEdit || canDelete) && (jsxs(DropdownMenu, { children: [jsx(DropdownMenuTrigger, { asChild: true, children: jsx(Button$1, { type: "button", variant: "ghost", size: "icon", "aria-label": labels.actionsAria(criterion.name), children: jsx(Ellipsis, { className: "h-4 w-4" }) }) }), jsxs(DropdownMenuContent, { align: "end", children: [canEdit && (jsxs(DropdownMenuItem, { onClick: () => setCriterionToEdit(criterion), children: [jsx(Pencil, { className: "mr-2 h-4 w-4" }), labels.edit] })), canDelete && (jsxs(DropdownMenuItem, { className: "text-red-600 focus:text-red-700", disabled: isLastCriterion, onClick: () => {
268909
269757
  // Radix marks the item disabled but a raw click can
268910
269758
  // still land — the backend refuses to delete the
268911
269759
  // last criterion, so hard-guard here too.
268912
269760
  if (isLastCriterion)
268913
269761
  return;
268914
269762
  setCriterionToDelete(criterion);
268915
- }, children: [jsx(Trash2, { className: "mr-2 h-4 w-4" }), labels.delete] })] })] }) })] }, criterion.id)))) })] })) }) }) }), criteria.length > 0 && !isLoading && (jsxs(Fragment$1, { children: [jsxs("div", { className: "flex items-center justify-between pt-1 text-xs", children: [jsx("span", { className: "font-medium text-gray-900", children: labels.totalPoints.replace('{total}', String(totalPoints)) }), jsx("span", { className: "text-gray-500", children: labels.scoreHint.replace('{total}', String(totalPoints)) })] }), isLastCriterion && (
269763
+ }, children: [jsx(Trash2, { className: "mr-2 h-4 w-4" }), labels.delete] }))] })] })) })] }, criterion.id)))) })] })) }) }) }), criteria.length > 0 && !isLoading && (jsxs(Fragment$1, { children: [jsxs("div", { className: "flex items-center justify-between pt-1 text-xs", children: [jsx("span", { className: "font-medium text-gray-900", children: labels.totalPoints.replace('{total}', String(totalPoints)) }), jsx("span", { className: "text-gray-500", children: labels.scoreHint.replace('{total}', String(totalPoints)) })] }), isLastCriterion && (
268916
269764
  // The backend refuses to delete the last criterion — explain why
268917
269765
  // its Delete menu action is disabled.
268918
269766
  jsx("p", { className: "text-xs text-gray-500", "data-testid": "grader-last-criterion-hint", children: labels.lastCriterionHint }))] })), jsx(CriterionModal, { isOpen: showCreate, onClose: () => setShowCreate(false), criterion: null, labels: labels.modal, isSaving: isMutating, onSave: onAdd }), jsx(CriterionModal, { isOpen: criterionToEdit !== null, onClose: () => setCriterionToEdit(null), criterion: criterionToEdit, labels: labels.modal, isSaving: isMutating, onSave: (values) => onEdit(criterionToEdit.id, values) }), jsx(CriterionDeleteModal, { isOpen: criterionToDelete !== null, onClose: () => setCriterionToDelete(null), criterion: criterionToDelete, labels: labels.deleteModal, isDeleting: isMutating, onConfirm: onDelete })] }));
268919
269767
  }
268920
269768
 
268921
- function SetupSection({ labels, config, isSaving, onSave }) {
269769
+ function SetupSection({ labels, config, isSaving, canSave: canSavePermission, onSave, }) {
268922
269770
  const [gradingMode, setGradingMode] = React__default.useState('submission');
268923
269771
  const [feedbackMode, setFeedbackMode] = React__default.useState('both');
268924
269772
  const [instructions, setInstructions] = React__default.useState('');
@@ -268937,11 +269785,11 @@ function SetupSection({ labels, config, isSaving, onSave }) {
268937
269785
  const canSave = instructions.trim().length > 0 && isDirty && !isSaving;
268938
269786
  return (jsxs("div", { className: "space-y-4", "data-testid": "grader-setup-section", children: [jsxs("div", { className: "grid grid-cols-1 gap-4 md:grid-cols-2", children: [jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { className: "text-xs font-medium text-gray-700", children: labels.gradingMode.label }), jsxs(Select$1, { value: gradingMode, onValueChange: (value) => setGradingMode(value), children: [jsx(SelectTrigger, { className: "h-9", "data-testid": "grader-grading-mode-select", children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "submission", children: labels.gradingMode.options.submission }), jsx(SelectItem, { value: "conversation", children: labels.gradingMode.options.conversation })] })] }), jsx("p", { className: "text-xs text-gray-500", children: gradingMode === 'submission'
268939
269787
  ? labels.gradingMode.help.submission
268940
- : labels.gradingMode.help.conversation })] }), jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { className: "text-xs font-medium text-gray-700", children: labels.feedbackMode.label }), jsxs(Select$1, { value: feedbackMode, onValueChange: (value) => setFeedbackMode(value), children: [jsx(SelectTrigger, { className: "h-9", "data-testid": "grader-feedback-mode-select", children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "overall", children: labels.feedbackMode.options.overall }), jsx(SelectItem, { value: "per_criteria", children: labels.feedbackMode.options.perCriteria }), jsx(SelectItem, { value: "both", children: labels.feedbackMode.options.both })] })] }), jsx("p", { className: "text-xs text-gray-500", children: labels.feedbackMode.help })] })] }), jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { className: "text-xs font-medium text-gray-700", children: labels.instructions.label }), jsx(Textarea, { value: instructions, onChange: (e) => setInstructions(e.target.value), placeholder: labels.instructions.placeholder, rows: 4, "data-testid": "grader-instructions-textarea" }), jsx("p", { className: "text-xs text-gray-500", children: labels.instructions.help })] }), jsx("div", { className: "flex items-center justify-end", children: jsx("button", { type: "button", onClick: () => onSave({
269788
+ : labels.gradingMode.help.conversation })] }), jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { className: "text-xs font-medium text-gray-700", children: labels.feedbackMode.label }), jsxs(Select$1, { value: feedbackMode, onValueChange: (value) => setFeedbackMode(value), children: [jsx(SelectTrigger, { className: "h-9", "data-testid": "grader-feedback-mode-select", children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "overall", children: labels.feedbackMode.options.overall }), jsx(SelectItem, { value: "per_criteria", children: labels.feedbackMode.options.perCriteria }), jsx(SelectItem, { value: "both", children: labels.feedbackMode.options.both })] })] }), jsx("p", { className: "text-xs text-gray-500", children: labels.feedbackMode.help })] })] }), jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { className: "text-xs font-medium text-gray-700", children: labels.instructions.label }), jsx(Textarea, { value: instructions, onChange: (e) => setInstructions(e.target.value), placeholder: labels.instructions.placeholder, rows: 4, "data-testid": "grader-instructions-textarea" }), jsx("p", { className: "text-xs text-gray-500", children: labels.instructions.help })] }), canSavePermission && (jsx("div", { className: "flex items-center justify-end", children: jsx("button", { type: "button", onClick: () => onSave({
268941
269789
  grading_mode: gradingMode,
268942
269790
  feedback_mode: feedbackMode,
268943
269791
  grader_instructions: instructions,
268944
- }), disabled: !canSave, "data-testid": "grader-save-button", className: "inline-flex items-center gap-2 rounded-md bg-gradient-to-r from-[#2563EB] to-[#93C5FD] px-4 py-2 text-sm font-medium text-white hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50", children: isSaving ? labels.savingButton : labels.saveButton }) })] }));
269792
+ }), disabled: !canSave, "data-testid": "grader-save-button", className: "inline-flex items-center gap-2 rounded-md bg-gradient-to-r from-[#2563EB] to-[#93C5FD] px-4 py-2 text-sm font-medium text-white hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50", children: isSaving ? labels.savingButton : labels.saveButton }) }))] }));
268945
269793
  }
268946
269794
 
268947
269795
  /**
@@ -268958,7 +269806,7 @@ function errorDetail(error) {
268958
269806
  const detail = (_a = error === null || error === void 0 ? void 0 : error.data) === null || _a === void 0 ? void 0 : _a.detail;
268959
269807
  return typeof detail === 'string' ? detail : '';
268960
269808
  }
268961
- function useGrader({ tenantKey, mentorId, username, labels }) {
269809
+ function useGrader({ tenantKey, mentorId, username, labels, enableRBAC = false, rbacPermissions = {}, }) {
268962
269810
  var _a;
268963
269811
  const identityReady = Boolean(tenantKey && mentorId && username);
268964
269812
  const { data: mentorSettings, isLoading: isSettingsLoading } = useGetMentorSettingsQuery({
@@ -268995,6 +269843,25 @@ function useGrader({ tenantKey, mentorId, username, labels }) {
268995
269843
  const configNotFound = errorStatus(configError) === 404;
268996
269844
  const hasConfig = Boolean(config);
268997
269845
  const isForbidden = errorStatus(configError) === 403 || errorStatus(criteriaError) === 403;
269846
+ // Grader permissions are flat actions on the mentor resource
269847
+ // (`/mentors/{dbId}/#write_grader_config`, `#view_grade_results`, …) — the
269848
+ // same entry every other mentor check uses. The check endpoint may predate
269849
+ // these actions, so they are only enforced when the tree actually contains
269850
+ // the mentor entry; otherwise everything stays visible and the server's
269851
+ // 403s remain the source of truth.
269852
+ const mentorDbId = mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.mentor_id;
269853
+ const mentorEntry = rbacPermissions[`/mentors/${mentorDbId}/`];
269854
+ const allow = (action) => !mentorEntry ||
269855
+ checkRbacPermission(rbacPermissions, `/mentors/${mentorDbId}/#${action}`, enableRBAC);
269856
+ const canReadConfig = allow('read_grader_config');
269857
+ // First save POSTs (create_grader_config), later saves PATCH (write_grader_config).
269858
+ const canSaveConfig = allow(hasConfig ? 'write_grader_config' : 'create_grader_config');
269859
+ const canViewCriteria = allow('view_grader_criteria');
269860
+ const canAddCriterion = allow('create_grader_criteria');
269861
+ const canEditCriterion = allow('write_grader_criteria');
269862
+ const canDeleteCriterion = allow('delete_grader_criteria');
269863
+ const canViewResults = allow('view_grade_results');
269864
+ const canOverrideResults = allow('override_grade_results');
268998
269865
  const totalPoints = criteria.reduce((sum, criterion) => sum + (Number(criterion.points) || 0), 0);
268999
269866
  const toggleGrading = async (checked) => {
269000
269867
  var _a, _b, _c;
@@ -269104,6 +269971,14 @@ function useGrader({ tenantKey, mentorId, username, labels }) {
269104
269971
  return {
269105
269972
  isLoading: isSettingsLoading || isConfigLoading,
269106
269973
  isForbidden,
269974
+ canReadConfig,
269975
+ canSaveConfig,
269976
+ canViewCriteria,
269977
+ canAddCriterion,
269978
+ canEditCriterion,
269979
+ canDeleteCriterion,
269980
+ canViewResults,
269981
+ canOverrideResults,
269107
269982
  enabled,
269108
269983
  isToggling,
269109
269984
  toggleGrading,
@@ -269125,6 +270000,7 @@ function useGrader({ tenantKey, mentorId, username, labels }) {
269125
270000
  const GRADER_SUB_TABS = {
269126
270001
  setup: 'setup',
269127
270002
  rubric: 'rubric',
270003
+ results: 'results',
269128
270004
  };
269129
270005
  /**
269130
270006
  * Standalone top-level tab for the agent's grading setup: the in-tab toggle
@@ -269137,15 +270013,26 @@ const GRADER_SUB_TABS = {
269137
270013
  * rubric has at least one criterion — the tab surfaces a warning whenever
269138
270014
  * the tool is on but either of the other two is missing.
269139
270015
  */
269140
- function AgentGraderTab({ labels: labelsOverride, tenantKey: tenantKeyProp, mentorId: mentorIdProp, username: usernameProp, }) {
269141
- var _a, _b, _c;
270016
+ function AgentGraderTab({ labels: labelsOverride, tenantKey: tenantKeyProp, mentorId: mentorIdProp, username: usernameProp, enableRBAC: enableRBACProp, rbacPermissions: rbacPermissionsProp, }) {
270017
+ var _a, _b, _c, _d, _e;
269142
270018
  const ctx = useAgentSettingsOptional();
269143
270019
  const tenantKey = (_a = tenantKeyProp !== null && tenantKeyProp !== void 0 ? tenantKeyProp : ctx === null || ctx === void 0 ? void 0 : ctx.tenantKey) !== null && _a !== void 0 ? _a : '';
269144
270020
  const mentorId = (_b = mentorIdProp !== null && mentorIdProp !== void 0 ? mentorIdProp : ctx === null || ctx === void 0 ? void 0 : ctx.mentorId) !== null && _b !== void 0 ? _b : '';
269145
270021
  const username = (_c = usernameProp !== null && usernameProp !== void 0 ? usernameProp : ctx === null || ctx === void 0 ? void 0 : ctx.username) !== null && _c !== void 0 ? _c : '';
270022
+ const enableRBAC = (_d = enableRBACProp !== null && enableRBACProp !== void 0 ? enableRBACProp : ctx === null || ctx === void 0 ? void 0 : ctx.enableRBAC) !== null && _d !== void 0 ? _d : false;
270023
+ const rbacPermissions = (_e = rbacPermissionsProp !== null && rbacPermissionsProp !== void 0 ? rbacPermissionsProp : ctx === null || ctx === void 0 ? void 0 : ctx.rbacPermissions) !== null && _e !== void 0 ? _e : {};
269146
270024
  const t = useT();
269147
270025
  const labels = React__default.useMemo(() => resolveGraderTabLabels(labelsOverride, t), [labelsOverride, t]);
269148
- const { isLoading, isForbidden, enabled, isToggling, toggleGrading, config, hasConfig, isSavingConfig, saveConfig, criteria, isCriteriaLoading, totalPoints, addCriterion, editCriterion, removeCriterion, isMutatingCriteria, } = useGrader({ tenantKey, mentorId, username, labels });
270026
+ const { isLoading, isForbidden, enabled, isToggling, toggleGrading, config, hasConfig, isSavingConfig, saveConfig, criteria, isCriteriaLoading, totalPoints, addCriterion, editCriterion, removeCriterion, isMutatingCriteria, canReadConfig, canSaveConfig, canViewCriteria, canAddCriterion, canEditCriterion, canDeleteCriterion, canViewResults, canOverrideResults, } = useGrader({ tenantKey, mentorId, username, labels, enableRBAC, rbacPermissions });
270027
+ const defaultSubTab = canReadConfig
270028
+ ? GRADER_SUB_TABS.setup
270029
+ : canViewCriteria
270030
+ ? GRADER_SUB_TABS.rubric
270031
+ : GRADER_SUB_TABS.results;
270032
+ // With every grader view action denied, no sub-tab trigger renders — treat
270033
+ // it like a read denial rather than showing an empty shell (the default
270034
+ // tab's content would otherwise leak without its trigger).
270035
+ const nothingViewable = !canReadConfig && !canViewCriteria && !canViewResults;
269149
270036
  const showNoCriteriaWarning = enabled && hasConfig && !isCriteriaLoading && criteria.length === 0;
269150
270037
  const showNoConfigWarning = enabled && !isLoading && !hasConfig && !isForbidden;
269151
270038
  const warningMessage = showNoConfigWarning
@@ -269153,7 +270040,7 @@ function AgentGraderTab({ labels: labelsOverride, tenantKey: tenantKeyProp, ment
269153
270040
  : showNoCriteriaWarning
269154
270041
  ? labels.warnings.noCriteria
269155
270042
  : null;
269156
- return (jsxs(Fragment$1, { children: [jsx("div", { className: "flex h-[73px] flex-shrink-0 items-center border-b border-gray-200 bg-white p-4 lg:block", children: jsxs("div", { children: [jsx("h3", { className: "mb-1 text-base font-medium text-gray-900", children: labels.header.title }), jsx("p", { className: "text-xs text-gray-600", children: labels.header.description })] }) }), jsx("div", { className: "flex-1 space-y-4 p-3 lg:p-4", style: { overflowY: 'auto', overflowX: 'hidden' }, "data-testid": "grader-tab-body", children: isLoading ? (jsx("div", { className: "flex items-center justify-center py-12", "data-testid": "grader-tab-loading", children: jsx(Spinner, {}) })) : isForbidden ? (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": "grader-tab-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(CapabilityGate, { title: labels.capability.title, description: labels.capability.description, offHint: labels.capability.offHint, enabled: enabled, onToggle: toggleGrading, disabled: isToggling, ariaLabel: labels.capability.title, toggleTestId: "grader-capability-toggle", children: [warningMessage && (jsxs("div", { className: "mb-4 flex items-start gap-2 rounded-lg border border-amber-200 bg-amber-50 p-3", "data-testid": "grader-misconfigured-warning", children: [jsx(TriangleAlert, { className: "mt-0.5 h-4 w-4 flex-shrink-0 text-amber-600" }), jsx("p", { className: "text-xs leading-relaxed text-amber-800", children: warningMessage })] })), jsxs(Tabs, { defaultValue: GRADER_SUB_TABS.setup, className: "w-full", children: [jsxs(TabsList, { "aria-label": labels.header.title, "data-testid": "grader-sub-tabs", children: [jsx(TabsTrigger, { value: GRADER_SUB_TABS.setup, "data-testid": "grader-sub-tab-setup", children: labels.subTabs.setup }), jsx(TabsTrigger, { value: GRADER_SUB_TABS.rubric, "data-testid": "grader-sub-tab-rubric", children: labels.subTabs.rubric })] }), jsx(TabsContent, { value: GRADER_SUB_TABS.setup, className: "space-y-4", children: jsx(SetupSection, { labels: labels.config, config: config, isSaving: isSavingConfig, onSave: saveConfig }) }), jsx(TabsContent, { value: GRADER_SUB_TABS.rubric, className: "space-y-4", children: jsx(RubricSection, { labels: labels.criteria, criteria: criteria, hasConfig: hasConfig, isLoading: isCriteriaLoading, isMutating: isMutatingCriteria, totalPoints: totalPoints, onAdd: addCriterion, onEdit: editCriterion, onDelete: removeCriterion }) })] })] })) })] }));
270043
+ return (jsxs(Fragment$1, { children: [jsx("div", { className: "flex h-[73px] flex-shrink-0 items-center border-b border-gray-200 bg-white p-4 lg:block", children: jsxs("div", { children: [jsx("h3", { className: "mb-1 text-base font-medium text-gray-900", children: labels.header.title }), jsx("p", { className: "text-xs text-gray-600", children: labels.header.description })] }) }), jsx("div", { className: "flex-1 space-y-4 p-3 lg:p-4", style: { overflowY: 'auto', overflowX: 'hidden' }, "data-testid": "grader-tab-body", children: isLoading ? (jsx("div", { className: "flex items-center justify-center py-12", "data-testid": "grader-tab-loading", children: jsx(Spinner, {}) })) : isForbidden || nothingViewable ? (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": "grader-tab-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(CapabilityGate, { title: labels.capability.title, description: labels.capability.description, offHint: labels.capability.offHint, enabled: enabled, onToggle: toggleGrading, disabled: isToggling, ariaLabel: labels.capability.title, toggleTestId: "grader-capability-toggle", children: [warningMessage && (jsxs("div", { className: "mb-4 flex items-start gap-2 rounded-lg border border-amber-200 bg-amber-50 p-3", "data-testid": "grader-misconfigured-warning", children: [jsx(TriangleAlert, { className: "mt-0.5 h-4 w-4 flex-shrink-0 text-amber-600" }), jsx("p", { className: "text-xs leading-relaxed text-amber-800", children: warningMessage })] })), jsxs(Tabs, { defaultValue: defaultSubTab, className: "w-full", children: [jsxs(TabsList, { "aria-label": labels.header.title, "data-testid": "grader-sub-tabs", children: [canReadConfig && (jsx(TabsTrigger, { value: GRADER_SUB_TABS.setup, "data-testid": "grader-sub-tab-setup", children: labels.subTabs.setup })), canViewCriteria && (jsx(TabsTrigger, { value: GRADER_SUB_TABS.rubric, "data-testid": "grader-sub-tab-rubric", children: labels.subTabs.rubric })), canViewResults && (jsx(TabsTrigger, { value: GRADER_SUB_TABS.results, "data-testid": "grader-sub-tab-results", children: labels.subTabs.results }))] }), jsx(TabsContent, { value: GRADER_SUB_TABS.setup, className: "space-y-4", children: jsx(SetupSection, { labels: labels.config, config: config, isSaving: isSavingConfig, canSave: canSaveConfig, onSave: saveConfig }) }), jsx(TabsContent, { value: GRADER_SUB_TABS.rubric, className: "space-y-4", children: jsx(RubricSection, { labels: labels.criteria, criteria: criteria, hasConfig: hasConfig, isLoading: isCriteriaLoading, isMutating: isMutatingCriteria, totalPoints: totalPoints, canAdd: canAddCriterion, canEdit: canEditCriterion, canDelete: canDeleteCriterion, onAdd: addCriterion, onEdit: editCriterion, onDelete: removeCriterion }) }), jsx(TabsContent, { value: GRADER_SUB_TABS.results, className: "space-y-4", children: jsx(ResultsSection, { labels: labels.results, tenantKey: tenantKey, mentorId: mentorId, username: username, canOverride: canOverrideResults }) })] })] })) })] }));
269157
270044
  }
269158
270045
 
269159
270046
  function ApiKeyModal({ isOpen, onClose, apiKey, labels }) {