@iblai/iblai-js 2.4.4 → 2.4.5
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.
- package/dist/data-layer/playwright/agent-skills-helpers.d.ts +23 -10
- package/dist/data-layer/playwright/index.d.ts +3 -1
- package/dist/data-layer/playwright/spend-limits-helpers.d.ts +275 -0
- package/dist/playwright/index.cjs +770 -66
- package/dist/playwright/index.cjs.map +1 -1
- package/dist/playwright/index.d.ts +300 -12
- package/dist/playwright/index.esm.js +737 -67
- package/dist/playwright/index.esm.js.map +1 -1
- package/dist/playwright/playwright/agent-skills-helpers.d.ts +23 -10
- package/dist/playwright/playwright/index.d.ts +3 -1
- package/dist/playwright/playwright/spend-limits-helpers.d.ts +275 -0
- package/dist/security/playwright/agent-skills-helpers.d.ts +23 -10
- package/dist/security/playwright/index.d.ts +3 -1
- package/dist/security/playwright/spend-limits-helpers.d.ts +275 -0
- package/dist/web-containers/playwright/agent-skills-helpers.d.ts +23 -10
- package/dist/web-containers/playwright/index.d.ts +3 -1
- package/dist/web-containers/playwright/spend-limits-helpers.d.ts +275 -0
- package/dist/web-containers/source/index.esm.js +8835 -7168
- package/dist/web-containers/source/next/index.esm.js +1916 -241
- package/dist/web-utils/playwright/agent-skills-helpers.d.ts +23 -10
- package/dist/web-utils/playwright/index.d.ts +3 -1
- package/dist/web-utils/playwright/spend-limits-helpers.d.ts +275 -0
- package/package.json +5 -5
|
@@ -2,7 +2,7 @@ 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, 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, useGetMentorSkillAssignmentsInfiniteQuery, MENTOR_SKILL_ASSIGNMENTS_PAGE_SIZE, 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';
|
|
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, useGetAgentSpendCapQuery, useGetTenantSpendCapQuery, useListAgentUserSpendCapsQuery, useUpsertAgentSpendCapMutation, useDeleteAgentSpendCapMutation, useUpsertTenantSpendCapMutation, useDeleteTenantSpendCapMutation, useUpsertUserSpendCapMutation, useDeleteUserSpendCapMutation, useGetFinancialStatsQuery, useGetMentorsQuery, useListAgentSpendCapsQuery, useGetCreditTransactionsQuery, 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, useGetMentorSkillAssignmentsQuery, useGetAgentSkillsQuery, useCreateMentorSkillAssignmentMutation, useUpdateMentorSkillAssignmentMutation, useDeleteMentorSkillAssignmentMutation, useCreateAgentSkillMutation, useUpdateAgentSkillMutation, useDeleteAgentSkillMutation, filterSlashSkills, useGetMentorSkillAssignmentsInfiniteQuery, MENTOR_SKILL_ASSIGNMENTS_PAGE_SIZE, 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, useGetSpendCapStatusQuery, 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
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';
|
|
@@ -98333,7 +98333,13 @@ var chatIndex$3 = {
|
|
|
98333
98333
|
confirmScreenShareTitle: "Confirm Screen Sharing",
|
|
98334
98334
|
confirmScreenShareDescription: "Would you like to start a screen sharing with your agent?",
|
|
98335
98335
|
cancel: "Cancel",
|
|
98336
|
-
confirm: "Confirm"
|
|
98336
|
+
confirm: "Confirm",
|
|
98337
|
+
spendCapLimitDay: "You've reached today's usage limit for this assistant.",
|
|
98338
|
+
spendCapLimitWeek: "You've reached this week's usage limit for this assistant.",
|
|
98339
|
+
spendCapLimitMonth: "You've reached this month's usage limit for this assistant.",
|
|
98340
|
+
spendCapLimitYear: "You've reached this year's usage limit for this assistant.",
|
|
98341
|
+
spendCapLimitGeneric: "You've reached the usage limit for this assistant.",
|
|
98342
|
+
spendCapContactAdmin: "Please try again after the limit resets, or contact your administrator."
|
|
98337
98343
|
};
|
|
98338
98344
|
var chatInputForm$3 = {
|
|
98339
98345
|
uploadingOneFile: "Uploading 1 file...",
|
|
@@ -98536,6 +98542,8 @@ var clawSandboxAgentSkills$3 = {
|
|
|
98536
98542
|
fieldCategoryPlaceholder: "e.g. web, code, data",
|
|
98537
98543
|
privateToAgent: "Only This Agent",
|
|
98538
98544
|
privateToAgentHint: "Private skills are available to this agent only and take precedence over platform skills with the same slug.",
|
|
98545
|
+
privateToOtherAgentHint: "This skill is private to another agent, so this setting cannot be changed here.",
|
|
98546
|
+
privateToOtherAgentTooltip: "This skill is private to another agent and cannot be added here.",
|
|
98539
98547
|
instructionHint: "The playbook the agent follows when the skill is used. Edits apply immediately, including in conversations already in progress.",
|
|
98540
98548
|
filesHint: "Optional resources the agent can use with this skill. References and scripts are text files; assets are uploaded files.",
|
|
98541
98549
|
filesAfterCreateHint: "You can attach resources to this skill after creating it (reopen it with Edit).",
|
|
@@ -100353,7 +100361,8 @@ var profileBilling$3 = {
|
|
|
100353
100361
|
addCredits: "Add Credits",
|
|
100354
100362
|
manageBilling: "Manage Billing",
|
|
100355
100363
|
availableLabel: "Available",
|
|
100356
|
-
|
|
100364
|
+
creditsUsdEquivalent: "≈ {amount}",
|
|
100365
|
+
creditsConversionHint: "{credits} credits = $1",
|
|
100357
100366
|
creditsUnit: "{amount} Credits",
|
|
100358
100367
|
consumedLabel: "Consumed",
|
|
100359
100368
|
usedThisPeriod: "Used this period",
|
|
@@ -100365,10 +100374,17 @@ var profileBilling$3 = {
|
|
|
100365
100374
|
disabled: "Disabled",
|
|
100366
100375
|
manageUsage: "Manage Usage",
|
|
100367
100376
|
thresholdLabel: "Threshold",
|
|
100377
|
+
thresholdHint: "When your available credits fall below this amount, a top-up starts automatically",
|
|
100368
100378
|
rechargeAmountLabel: "Recharge amount",
|
|
100379
|
+
rechargeAmountHint: "Charged to your payment method and added to your credits with each top-up",
|
|
100369
100380
|
spendingLimitLabel: "Spending limit",
|
|
100381
|
+
spendingLimitHint: "The most auto recharge can charge per billing period",
|
|
100370
100382
|
unlimited: "Unlimited",
|
|
100371
|
-
autoRechargeDisabledMessage: "Auto recharge is currently disabled. Enable it to keep your balance topped up without interruption."
|
|
100383
|
+
autoRechargeDisabledMessage: "Auto recharge is currently disabled. Enable it to keep your balance topped up without interruption.",
|
|
100384
|
+
billingTabsAriaLabel: "Billing sections",
|
|
100385
|
+
tabPlanAndCredits: "Plan & Credits",
|
|
100386
|
+
tabSpendLimits: "Spend Limits",
|
|
100387
|
+
tabAgentLimits: "Agent Limits"
|
|
100372
100388
|
};
|
|
100373
100389
|
var profileCatalogInviteModal$3 = {
|
|
100374
100390
|
title: "Catalog Invite",
|
|
@@ -101312,6 +101328,156 @@ var skillsProfileSkillsBox$3 = {
|
|
|
101312
101328
|
desired: "Desired",
|
|
101313
101329
|
noDesiredSkills: "You don't have any desired skills yet."
|
|
101314
101330
|
};
|
|
101331
|
+
var spendCapUsage$3 = {
|
|
101332
|
+
scopeTenant: "Workspace",
|
|
101333
|
+
scopeAgent: "This agent",
|
|
101334
|
+
scopeUserAgent: "Your usage on this agent",
|
|
101335
|
+
intervalDay: "today",
|
|
101336
|
+
intervalWeek: "this week",
|
|
101337
|
+
intervalMonth: "this month",
|
|
101338
|
+
intervalYear: "this year",
|
|
101339
|
+
percentUsed: "{percent}% used",
|
|
101340
|
+
blockedBanner: "The usage limit has been reached. New chats and training are paused until the period resets.",
|
|
101341
|
+
warningBanner: "Usage is nearing its limit."
|
|
101342
|
+
};
|
|
101343
|
+
var spendCapsTabLabels$3 = {
|
|
101344
|
+
header: {
|
|
101345
|
+
title: "Billing",
|
|
101346
|
+
description: "Set spend limits for this agent and its users, and see how much has been used."
|
|
101347
|
+
},
|
|
101348
|
+
capability: {
|
|
101349
|
+
title: "Spend Limit",
|
|
101350
|
+
description: "Put a ceiling on AI spend. While this limit is on, everything it covers counts toward the amount below — and when the limit is reached, you choose whether new requests are blocked or admins are simply alerted.",
|
|
101351
|
+
offHint: "Turn it on to start tracking and enforcing this limit. Your setup here is kept safe if you later turn it off."
|
|
101352
|
+
},
|
|
101353
|
+
subTabs: {
|
|
101354
|
+
agent: "This Agent",
|
|
101355
|
+
users: "Per User"
|
|
101356
|
+
},
|
|
101357
|
+
tenantSections: {
|
|
101358
|
+
workspace: {
|
|
101359
|
+
title: "Workspace Spend Limit",
|
|
101360
|
+
description: "One limit covering everything all agents and users spend in this workspace. Per-agent and per-user limits live in each agent’s settings under Billing."
|
|
101361
|
+
},
|
|
101362
|
+
noCapStats: {
|
|
101363
|
+
spentThisMonth: "Spent This Month",
|
|
101364
|
+
spentAllTime: "Spent All Time",
|
|
101365
|
+
hint: "Across all agents and users in this workspace"
|
|
101366
|
+
},
|
|
101367
|
+
agents: {
|
|
101368
|
+
title: "Agent Spend Limits",
|
|
101369
|
+
description: "Agents with a spend limit configured in this workspace. Open one to manage its limit and its per-user limits.",
|
|
101370
|
+
filter: {
|
|
101371
|
+
label: "Search Agents",
|
|
101372
|
+
placeholder: "Search Agents…",
|
|
101373
|
+
typeToSearch: "Type at least 2 characters to search",
|
|
101374
|
+
searching: "Searching agents…",
|
|
101375
|
+
noResults: "No agents found",
|
|
101376
|
+
clear: "Clear Agent Filter"
|
|
101377
|
+
},
|
|
101378
|
+
table: {
|
|
101379
|
+
agentColumn: "Agent",
|
|
101380
|
+
limitColumn: "Limit",
|
|
101381
|
+
spentColumn: "Spent",
|
|
101382
|
+
remainingColumn: "Remaining",
|
|
101383
|
+
statusColumn: "Status"
|
|
101384
|
+
},
|
|
101385
|
+
emptyState: "No agent spend limits yet. Search for an agent above to set one.",
|
|
101386
|
+
emptyFiltered: "No spend limit for this agent yet.",
|
|
101387
|
+
setLimitButton: "Set Spend Limit",
|
|
101388
|
+
manageAria: "Manage spend limits for {name}",
|
|
101389
|
+
statusToggleAria: "Toggle spend limit for {name}"
|
|
101390
|
+
}
|
|
101391
|
+
},
|
|
101392
|
+
form: {
|
|
101393
|
+
limitLabel: "Spend Limit (USD)",
|
|
101394
|
+
limitPlaceholder: "e.g. 250.00",
|
|
101395
|
+
limitError: "Enter an amount greater than 0",
|
|
101396
|
+
intervalLabel: "Resets Every",
|
|
101397
|
+
intervalOptions: {
|
|
101398
|
+
day: "Day",
|
|
101399
|
+
week: "Week",
|
|
101400
|
+
month: "Month",
|
|
101401
|
+
year: "Year"
|
|
101402
|
+
},
|
|
101403
|
+
enforcementLabel: "When the Limit Is Reached",
|
|
101404
|
+
enforcementOptions: {
|
|
101405
|
+
block: "Block Requests",
|
|
101406
|
+
alertOnly: "Alert Only"
|
|
101407
|
+
},
|
|
101408
|
+
enforcementHelp: {
|
|
101409
|
+
block: "New chat and training requests are refused until the period resets.",
|
|
101410
|
+
alertOnly: "Nothing is blocked — admins are notified when spend nears the limit."
|
|
101411
|
+
},
|
|
101412
|
+
thresholdsLabel: "Alert At (% of Limit)",
|
|
101413
|
+
thresholdsPlaceholder: "e.g. 80, 95",
|
|
101414
|
+
thresholdsHelp: "Comma-separated percentages at which admins get a near-limit alert.",
|
|
101415
|
+
thresholdsError: "Use comma-separated numbers between 1 and 100",
|
|
101416
|
+
noCapHint: "No spend limit configured yet — set an amount and save to create one.",
|
|
101417
|
+
save: "Save",
|
|
101418
|
+
saving: "Saving…",
|
|
101419
|
+
remove: "Delete Limit",
|
|
101420
|
+
removing: "Deleting…",
|
|
101421
|
+
cancel: "Cancel"
|
|
101422
|
+
},
|
|
101423
|
+
usage: {
|
|
101424
|
+
title: "Spend Limit Usage",
|
|
101425
|
+
periodTitles: {
|
|
101426
|
+
day: "Daily Usage",
|
|
101427
|
+
week: "Weekly Usage",
|
|
101428
|
+
month: "Monthly Usage",
|
|
101429
|
+
year: "Yearly Usage"
|
|
101430
|
+
},
|
|
101431
|
+
spent: "Spent: {amount}",
|
|
101432
|
+
remaining: "Remaining: {amount}",
|
|
101433
|
+
percentUsed: "{percent}% of limit used",
|
|
101434
|
+
exceededBadge: "Limit Reached",
|
|
101435
|
+
alertOnlyBadge: "Alert Only",
|
|
101436
|
+
disabledBadge: "Off"
|
|
101437
|
+
},
|
|
101438
|
+
users: {
|
|
101439
|
+
sectionTitle: "Per-User Limits",
|
|
101440
|
+
sectionDescription: "Limit what specific users can spend on this agent.",
|
|
101441
|
+
addButton: "Add User Limit",
|
|
101442
|
+
addTitle: "New User Limit",
|
|
101443
|
+
editTitle: "Edit User Limit",
|
|
101444
|
+
cancel: "Cancel",
|
|
101445
|
+
emptyState: "No per-user limits yet. Add one to limit a specific user on this agent.",
|
|
101446
|
+
edit: "Edit",
|
|
101447
|
+
"delete": "Delete",
|
|
101448
|
+
table: {
|
|
101449
|
+
userColumn: "User",
|
|
101450
|
+
limitColumn: "Limit",
|
|
101451
|
+
statusColumn: "Status"
|
|
101452
|
+
},
|
|
101453
|
+
actionsAria: "Actions for {username}",
|
|
101454
|
+
statusToggleAria: "Toggle spend limit for {username}",
|
|
101455
|
+
picker: {
|
|
101456
|
+
label: "User",
|
|
101457
|
+
placeholder: "Search Users…",
|
|
101458
|
+
typeToSearch: "Type at least 2 characters to search",
|
|
101459
|
+
searching: "Searching users…",
|
|
101460
|
+
noResults: "No users found",
|
|
101461
|
+
clear: "Change User"
|
|
101462
|
+
}
|
|
101463
|
+
},
|
|
101464
|
+
denied: {
|
|
101465
|
+
title: "No Access to Spend Limits",
|
|
101466
|
+
description: "You don't have permission to view spend limits here. Ask an administrator for access."
|
|
101467
|
+
},
|
|
101468
|
+
deleteModal: {
|
|
101469
|
+
title: "Delete Spend Limit",
|
|
101470
|
+
description: "Spending will no longer be limited or tracked against this limit.",
|
|
101471
|
+
cancel: "Cancel",
|
|
101472
|
+
confirm: "Delete"
|
|
101473
|
+
},
|
|
101474
|
+
toasts: {
|
|
101475
|
+
saved: "Spend limit saved",
|
|
101476
|
+
saveError: "Couldn't save the spend limit",
|
|
101477
|
+
deleted: "Spend limit deleted",
|
|
101478
|
+
deleteError: "Couldn't delete the spend limit"
|
|
101479
|
+
}
|
|
101480
|
+
};
|
|
101315
101481
|
var tabsAlertstab$3 = {
|
|
101316
101482
|
searchAlertsPlaceholder: "Search alerts",
|
|
101317
101483
|
searchAlertsAriaLabel: "Search alerts",
|
|
@@ -103339,6 +103505,8 @@ var en = {
|
|
|
103339
103505
|
skillsProfileResumeBox: skillsProfileResumeBox$3,
|
|
103340
103506
|
skillsProfileSkillDetailModal: skillsProfileSkillDetailModal$3,
|
|
103341
103507
|
skillsProfileSkillsBox: skillsProfileSkillsBox$3,
|
|
103508
|
+
spendCapUsage: spendCapUsage$3,
|
|
103509
|
+
spendCapsTabLabels: spendCapsTabLabels$3,
|
|
103342
103510
|
tabsAlertstab: tabsAlertstab$3,
|
|
103343
103511
|
tabsDisclaimersTab: tabsDisclaimersTab$3,
|
|
103344
103512
|
tabsEmbedTab: tabsEmbedTab$3,
|
|
@@ -105069,7 +105237,13 @@ var chatIndex$2 = {
|
|
|
105069
105237
|
confirmScreenShareTitle: "Confirmer le partage d'écran",
|
|
105070
105238
|
confirmScreenShareDescription: "Souhaitez-vous démarrer un partage d'écran avec votre agent ?",
|
|
105071
105239
|
cancel: "Annuler",
|
|
105072
|
-
confirm: "Confirmer"
|
|
105240
|
+
confirm: "Confirmer",
|
|
105241
|
+
spendCapLimitDay: "Vous avez atteint la limite d'utilisation du jour pour cet assistant.",
|
|
105242
|
+
spendCapLimitWeek: "Vous avez atteint la limite d'utilisation de la semaine pour cet assistant.",
|
|
105243
|
+
spendCapLimitMonth: "Vous avez atteint la limite d'utilisation du mois pour cet assistant.",
|
|
105244
|
+
spendCapLimitYear: "Vous avez atteint la limite d'utilisation de l'année pour cet assistant.",
|
|
105245
|
+
spendCapLimitGeneric: "Vous avez atteint la limite d'utilisation de cet assistant.",
|
|
105246
|
+
spendCapContactAdmin: "Réessayez après la réinitialisation de la limite ou contactez votre administrateur."
|
|
105073
105247
|
};
|
|
105074
105248
|
var chatInputForm$2 = {
|
|
105075
105249
|
uploadingOneFile: "Téléversement de 1 fichier...",
|
|
@@ -105272,6 +105446,8 @@ var clawSandboxAgentSkills$2 = {
|
|
|
105272
105446
|
fieldCategoryPlaceholder: "p. ex. web, code, données",
|
|
105273
105447
|
privateToAgent: "Cet agent uniquement",
|
|
105274
105448
|
privateToAgentHint: "Les compétences privées ne sont disponibles que pour cet agent et priment sur les compétences de la plateforme portant le même slug.",
|
|
105449
|
+
privateToOtherAgentHint: "Cette compétence est privée d'un autre agent, ce réglage ne peut donc pas être modifié ici.",
|
|
105450
|
+
privateToOtherAgentTooltip: "Cette compétence est privée d'un autre agent et ne peut pas être ajoutée ici.",
|
|
105275
105451
|
instructionHint: "Le guide que l'agent suit lorsque la compétence est utilisée. Les modifications s'appliquent immédiatement, y compris dans les conversations en cours.",
|
|
105276
105452
|
filesHint: "Ressources facultatives que l'agent peut utiliser avec cette compétence. Les références et les scripts sont des fichiers texte ; les assets sont des fichiers téléversés.",
|
|
105277
105453
|
filesAfterCreateHint: "Vous pouvez joindre des ressources à cette compétence après sa création (rouvrez-la avec Modifier).",
|
|
@@ -107089,7 +107265,8 @@ var profileBilling$2 = {
|
|
|
107089
107265
|
addCredits: "Ajouter des crédits",
|
|
107090
107266
|
manageBilling: "Gérer la facturation",
|
|
107091
107267
|
availableLabel: "Disponibles",
|
|
107092
|
-
|
|
107268
|
+
creditsUsdEquivalent: "≈ {amount}",
|
|
107269
|
+
creditsConversionHint: "{credits} crédits = 1 $",
|
|
107093
107270
|
creditsUnit: "{amount} Crédits",
|
|
107094
107271
|
consumedLabel: "Consommés",
|
|
107095
107272
|
usedThisPeriod: "Utilisés cette période",
|
|
@@ -107101,10 +107278,17 @@ var profileBilling$2 = {
|
|
|
107101
107278
|
disabled: "Désactivé",
|
|
107102
107279
|
manageUsage: "Gérer l'utilisation",
|
|
107103
107280
|
thresholdLabel: "Seuil",
|
|
107281
|
+
thresholdHint: "Quand vos crédits disponibles passent sous ce montant, une recharge démarre automatiquement",
|
|
107104
107282
|
rechargeAmountLabel: "Montant de rechargement",
|
|
107283
|
+
rechargeAmountHint: "Débité de votre moyen de paiement et ajouté à vos crédits à chaque recharge",
|
|
107105
107284
|
spendingLimitLabel: "Limite de dépenses",
|
|
107285
|
+
spendingLimitHint: "Le maximum que la recharge automatique peut débiter par période de facturation",
|
|
107106
107286
|
unlimited: "Illimité",
|
|
107107
|
-
autoRechargeDisabledMessage: "Le rechargement automatique est actuellement désactivé. Activez-le pour maintenir votre solde sans interruption."
|
|
107287
|
+
autoRechargeDisabledMessage: "Le rechargement automatique est actuellement désactivé. Activez-le pour maintenir votre solde sans interruption.",
|
|
107288
|
+
billingTabsAriaLabel: "Sections de facturation",
|
|
107289
|
+
tabPlanAndCredits: "Forfait et crédits",
|
|
107290
|
+
tabSpendLimits: "Limites de dépenses",
|
|
107291
|
+
tabAgentLimits: "Limites par agent"
|
|
107108
107292
|
};
|
|
107109
107293
|
var profileCatalogInviteModal$2 = {
|
|
107110
107294
|
title: "Invitation au catalogue",
|
|
@@ -108048,6 +108232,156 @@ var skillsProfileSkillsBox$2 = {
|
|
|
108048
108232
|
desired: "Souhaitées",
|
|
108049
108233
|
noDesiredSkills: "Vous n'avez pas encore de compétences souhaitées."
|
|
108050
108234
|
};
|
|
108235
|
+
var spendCapUsage$2 = {
|
|
108236
|
+
scopeTenant: "Espace de travail",
|
|
108237
|
+
scopeAgent: "Cet agent",
|
|
108238
|
+
scopeUserAgent: "Votre utilisation sur cet agent",
|
|
108239
|
+
intervalDay: "aujourd'hui",
|
|
108240
|
+
intervalWeek: "cette semaine",
|
|
108241
|
+
intervalMonth: "ce mois-ci",
|
|
108242
|
+
intervalYear: "cette année",
|
|
108243
|
+
percentUsed: "{percent}% utilisé",
|
|
108244
|
+
blockedBanner: "La limite d'utilisation est atteinte. Les nouveaux chats et l'entraînement sont suspendus jusqu'à la réinitialisation de la période.",
|
|
108245
|
+
warningBanner: "L'utilisation approche de sa limite."
|
|
108246
|
+
};
|
|
108247
|
+
var spendCapsTabLabels$2 = {
|
|
108248
|
+
header: {
|
|
108249
|
+
title: "Facturation",
|
|
108250
|
+
description: "Définissez des limites de dépenses pour cet agent et ses utilisateurs, et consultez la consommation."
|
|
108251
|
+
},
|
|
108252
|
+
capability: {
|
|
108253
|
+
title: "Limite de dépenses",
|
|
108254
|
+
description: "Fixez un plafond aux dépenses d'IA. Tant que cette limite est activée, tout ce qu'elle couvre compte dans le montant ci-dessous — et lorsque la limite est atteinte, vous choisissez de bloquer les nouvelles requêtes ou de simplement alerter les administrateurs.",
|
|
108255
|
+
offHint: "Activez-la pour commencer à suivre et appliquer cette limite. Votre configuration est conservée si vous la désactivez plus tard."
|
|
108256
|
+
},
|
|
108257
|
+
subTabs: {
|
|
108258
|
+
agent: "Cet agent",
|
|
108259
|
+
users: "Par utilisateur"
|
|
108260
|
+
},
|
|
108261
|
+
tenantSections: {
|
|
108262
|
+
workspace: {
|
|
108263
|
+
title: "Limite de dépenses de l'espace de travail",
|
|
108264
|
+
description: "Une seule limite couvrant tout ce que dépensent les agents et utilisateurs de cet espace de travail. Les limites par agent et par utilisateur se gèrent dans les réglages de chaque agent, sous Facturation."
|
|
108265
|
+
},
|
|
108266
|
+
noCapStats: {
|
|
108267
|
+
spentThisMonth: "Dépensé ce mois-ci",
|
|
108268
|
+
spentAllTime: "Dépensé au total",
|
|
108269
|
+
hint: "Sur l'ensemble des agents et utilisateurs de cet espace de travail"
|
|
108270
|
+
},
|
|
108271
|
+
agents: {
|
|
108272
|
+
title: "Limites de dépenses par agent",
|
|
108273
|
+
description: "Agents avec une limite de dépenses configurée dans cet espace de travail. Ouvrez-en un pour gérer sa limite et ses limites par utilisateur.",
|
|
108274
|
+
filter: {
|
|
108275
|
+
label: "Rechercher des agents",
|
|
108276
|
+
placeholder: "Rechercher des agents…",
|
|
108277
|
+
typeToSearch: "Saisissez au moins 2 caractères pour rechercher",
|
|
108278
|
+
searching: "Recherche d'agents…",
|
|
108279
|
+
noResults: "Aucun agent trouvé",
|
|
108280
|
+
clear: "Effacer le filtre d'agent"
|
|
108281
|
+
},
|
|
108282
|
+
table: {
|
|
108283
|
+
agentColumn: "Agent",
|
|
108284
|
+
limitColumn: "Limite",
|
|
108285
|
+
spentColumn: "Dépensé",
|
|
108286
|
+
remainingColumn: "Restant",
|
|
108287
|
+
statusColumn: "Statut"
|
|
108288
|
+
},
|
|
108289
|
+
emptyState: "Aucune limite de dépenses par agent pour l'instant. Recherchez un agent ci-dessus pour en définir une.",
|
|
108290
|
+
emptyFiltered: "Cet agent n'a pas encore de limite de dépenses.",
|
|
108291
|
+
setLimitButton: "Définir une limite de dépenses",
|
|
108292
|
+
manageAria: "Gérer les limites de dépenses de {name}",
|
|
108293
|
+
statusToggleAria: "Activer ou désactiver la limite de dépenses de {name}"
|
|
108294
|
+
}
|
|
108295
|
+
},
|
|
108296
|
+
form: {
|
|
108297
|
+
limitLabel: "Limite de dépenses (USD)",
|
|
108298
|
+
limitPlaceholder: "p. ex. 250.00",
|
|
108299
|
+
limitError: "Saisissez un montant supérieur à 0",
|
|
108300
|
+
intervalLabel: "Réinitialisation chaque",
|
|
108301
|
+
intervalOptions: {
|
|
108302
|
+
day: "Jour",
|
|
108303
|
+
week: "Semaine",
|
|
108304
|
+
month: "Mois",
|
|
108305
|
+
year: "Année"
|
|
108306
|
+
},
|
|
108307
|
+
enforcementLabel: "Quand la limite est atteinte",
|
|
108308
|
+
enforcementOptions: {
|
|
108309
|
+
block: "Bloquer les requêtes",
|
|
108310
|
+
alertOnly: "Alerte uniquement"
|
|
108311
|
+
},
|
|
108312
|
+
enforcementHelp: {
|
|
108313
|
+
block: "Les nouvelles requêtes de chat et d'entraînement sont refusées jusqu'à la réinitialisation de la période.",
|
|
108314
|
+
alertOnly: "Rien n'est bloqué : les administrateurs sont notifiés lorsque les dépenses approchent de la limite."
|
|
108315
|
+
},
|
|
108316
|
+
thresholdsLabel: "Alerter à (% de la limite)",
|
|
108317
|
+
thresholdsPlaceholder: "p. ex. 80, 95",
|
|
108318
|
+
thresholdsHelp: "Pourcentages séparés par des virgules auxquels les administrateurs reçoivent une alerte d'approche de la limite.",
|
|
108319
|
+
thresholdsError: "Utilisez des nombres séparés par des virgules entre 1 et 100",
|
|
108320
|
+
noCapHint: "Aucune limite de dépenses configurée pour l'instant — définissez un montant et enregistrez pour en créer une.",
|
|
108321
|
+
save: "Enregistrer",
|
|
108322
|
+
saving: "Enregistrement…",
|
|
108323
|
+
remove: "Supprimer la limite",
|
|
108324
|
+
removing: "Suppression…",
|
|
108325
|
+
cancel: "Annuler"
|
|
108326
|
+
},
|
|
108327
|
+
usage: {
|
|
108328
|
+
title: "Utilisation de la limite de dépenses",
|
|
108329
|
+
periodTitles: {
|
|
108330
|
+
day: "Utilisation quotidienne",
|
|
108331
|
+
week: "Utilisation hebdomadaire",
|
|
108332
|
+
month: "Utilisation mensuelle",
|
|
108333
|
+
year: "Utilisation annuelle"
|
|
108334
|
+
},
|
|
108335
|
+
spent: "Dépensé : {amount}",
|
|
108336
|
+
remaining: "Restant : {amount}",
|
|
108337
|
+
percentUsed: "{percent}% de la limite utilisée",
|
|
108338
|
+
exceededBadge: "Limite atteinte",
|
|
108339
|
+
alertOnlyBadge: "Alerte uniquement",
|
|
108340
|
+
disabledBadge: "Désactivé"
|
|
108341
|
+
},
|
|
108342
|
+
users: {
|
|
108343
|
+
sectionTitle: "Limites par utilisateur",
|
|
108344
|
+
sectionDescription: "Limitez ce que certains utilisateurs peuvent dépenser sur cet agent.",
|
|
108345
|
+
addButton: "Ajouter une limite utilisateur",
|
|
108346
|
+
addTitle: "Nouvelle limite utilisateur",
|
|
108347
|
+
editTitle: "Modifier la limite utilisateur",
|
|
108348
|
+
cancel: "Annuler",
|
|
108349
|
+
emptyState: "Aucune limite par utilisateur pour l'instant. Ajoutez-en une pour limiter un utilisateur précis sur cet agent.",
|
|
108350
|
+
edit: "Modifier",
|
|
108351
|
+
"delete": "Supprimer",
|
|
108352
|
+
table: {
|
|
108353
|
+
userColumn: "Utilisateur",
|
|
108354
|
+
limitColumn: "Limite",
|
|
108355
|
+
statusColumn: "Statut"
|
|
108356
|
+
},
|
|
108357
|
+
actionsAria: "Actions pour {username}",
|
|
108358
|
+
statusToggleAria: "Activer ou désactiver la limite de dépenses de {username}",
|
|
108359
|
+
picker: {
|
|
108360
|
+
label: "Utilisateur",
|
|
108361
|
+
placeholder: "Rechercher des utilisateurs…",
|
|
108362
|
+
typeToSearch: "Saisissez au moins 2 caractères pour rechercher",
|
|
108363
|
+
searching: "Recherche d'utilisateurs…",
|
|
108364
|
+
noResults: "Aucun utilisateur trouvé",
|
|
108365
|
+
clear: "Changer d'utilisateur"
|
|
108366
|
+
}
|
|
108367
|
+
},
|
|
108368
|
+
denied: {
|
|
108369
|
+
title: "Pas d'accès aux limites de dépenses",
|
|
108370
|
+
description: "Vous n'avez pas la permission de voir les limites de dépenses ici. Demandez l'accès à un administrateur."
|
|
108371
|
+
},
|
|
108372
|
+
deleteModal: {
|
|
108373
|
+
title: "Supprimer la limite de dépenses",
|
|
108374
|
+
description: "Les dépenses ne seront plus limitées ni suivies par cette limite.",
|
|
108375
|
+
cancel: "Annuler",
|
|
108376
|
+
confirm: "Supprimer"
|
|
108377
|
+
},
|
|
108378
|
+
toasts: {
|
|
108379
|
+
saved: "Limite de dépenses enregistrée",
|
|
108380
|
+
saveError: "Impossible d'enregistrer la limite de dépenses",
|
|
108381
|
+
deleted: "Limite de dépenses supprimée",
|
|
108382
|
+
deleteError: "Impossible de supprimer la limite de dépenses"
|
|
108383
|
+
}
|
|
108384
|
+
};
|
|
108051
108385
|
var tabsAlertstab$2 = {
|
|
108052
108386
|
searchAlertsPlaceholder: "Rechercher des alertes",
|
|
108053
108387
|
searchAlertsAriaLabel: "Rechercher des alertes",
|
|
@@ -109910,6 +110244,8 @@ var fr = {
|
|
|
109910
110244
|
skillsProfileResumeBox: skillsProfileResumeBox$2,
|
|
109911
110245
|
skillsProfileSkillDetailModal: skillsProfileSkillDetailModal$2,
|
|
109912
110246
|
skillsProfileSkillsBox: skillsProfileSkillsBox$2,
|
|
110247
|
+
spendCapUsage: spendCapUsage$2,
|
|
110248
|
+
spendCapsTabLabels: spendCapsTabLabels$2,
|
|
109913
110249
|
tabsAlertstab: tabsAlertstab$2,
|
|
109914
110250
|
tabsDisclaimersTab: tabsDisclaimersTab$2,
|
|
109915
110251
|
tabsEmbedTab: tabsEmbedTab$2,
|
|
@@ -111639,7 +111975,13 @@ var chatIndex$1 = {
|
|
|
111639
111975
|
confirmScreenShareTitle: "Confirmar uso compartido de pantalla",
|
|
111640
111976
|
confirmScreenShareDescription: "¿Deseas iniciar un uso compartido de pantalla con tu agente?",
|
|
111641
111977
|
cancel: "Cancelar",
|
|
111642
|
-
confirm: "Confirmar"
|
|
111978
|
+
confirm: "Confirmar",
|
|
111979
|
+
spendCapLimitDay: "Has alcanzado el límite de uso de hoy para este asistente.",
|
|
111980
|
+
spendCapLimitWeek: "Has alcanzado el límite de uso de esta semana para este asistente.",
|
|
111981
|
+
spendCapLimitMonth: "Has alcanzado el límite de uso de este mes para este asistente.",
|
|
111982
|
+
spendCapLimitYear: "Has alcanzado el límite de uso de este año para este asistente.",
|
|
111983
|
+
spendCapLimitGeneric: "Has alcanzado el límite de uso de este asistente.",
|
|
111984
|
+
spendCapContactAdmin: "Vuelve a intentarlo cuando el límite se reinicie o contacta con tu administrador."
|
|
111643
111985
|
};
|
|
111644
111986
|
var chatInputForm$1 = {
|
|
111645
111987
|
uploadingOneFile: "Subiendo 1 archivo...",
|
|
@@ -111842,6 +112184,8 @@ var clawSandboxAgentSkills$1 = {
|
|
|
111842
112184
|
fieldCategoryPlaceholder: "p. ej. web, código, datos",
|
|
111843
112185
|
privateToAgent: "Solo este agente",
|
|
111844
112186
|
privateToAgentHint: "Las habilidades privadas solo están disponibles para este agente y tienen prioridad sobre las habilidades de la plataforma con el mismo slug.",
|
|
112187
|
+
privateToOtherAgentHint: "Esta habilidad es privada de otro agente, por lo que este ajuste no se puede cambiar aquí.",
|
|
112188
|
+
privateToOtherAgentTooltip: "Esta habilidad es privada de otro agente y no se puede añadir aquí.",
|
|
111845
112189
|
instructionHint: "La guía que sigue el agente cuando se usa la habilidad. Los cambios se aplican de inmediato, incluso en conversaciones en curso.",
|
|
111846
112190
|
filesHint: "Recursos opcionales que el agente puede usar con esta habilidad. Las referencias y los scripts son archivos de texto; los assets son archivos subidos.",
|
|
111847
112191
|
filesAfterCreateHint: "Puedes adjuntar recursos a esta habilidad después de crearla (vuelve a abrirla con Editar).",
|
|
@@ -113659,7 +114003,8 @@ var profileBilling$1 = {
|
|
|
113659
114003
|
addCredits: "Agregar créditos",
|
|
113660
114004
|
manageBilling: "Gestionar facturación",
|
|
113661
114005
|
availableLabel: "Disponibles",
|
|
113662
|
-
|
|
114006
|
+
creditsUsdEquivalent: "≈ {amount}",
|
|
114007
|
+
creditsConversionHint: "{credits} créditos = $1",
|
|
113663
114008
|
creditsUnit: "{amount} Créditos",
|
|
113664
114009
|
consumedLabel: "Consumidos",
|
|
113665
114010
|
usedThisPeriod: "Usados este período",
|
|
@@ -113671,10 +114016,17 @@ var profileBilling$1 = {
|
|
|
113671
114016
|
disabled: "Deshabilitado",
|
|
113672
114017
|
manageUsage: "Gestionar uso",
|
|
113673
114018
|
thresholdLabel: "Umbral",
|
|
114019
|
+
thresholdHint: "Cuando tus créditos disponibles bajan de este importe, se inicia una recarga automática",
|
|
113674
114020
|
rechargeAmountLabel: "Monto de recarga",
|
|
114021
|
+
rechargeAmountHint: "Se cobra a tu método de pago y se añade a tus créditos en cada recarga",
|
|
113675
114022
|
spendingLimitLabel: "Límite de gasto",
|
|
114023
|
+
spendingLimitHint: "Lo máximo que la recarga automática puede cobrar por periodo de facturación",
|
|
113676
114024
|
unlimited: "Ilimitado",
|
|
113677
|
-
autoRechargeDisabledMessage: "La recarga automática está deshabilitada. Actívala para mantener tu saldo sin interrupciones."
|
|
114025
|
+
autoRechargeDisabledMessage: "La recarga automática está deshabilitada. Actívala para mantener tu saldo sin interrupciones.",
|
|
114026
|
+
billingTabsAriaLabel: "Secciones de facturación",
|
|
114027
|
+
tabPlanAndCredits: "Plan y créditos",
|
|
114028
|
+
tabSpendLimits: "Límites de gasto",
|
|
114029
|
+
tabAgentLimits: "Límites por agente"
|
|
113678
114030
|
};
|
|
113679
114031
|
var profileCatalogInviteModal$1 = {
|
|
113680
114032
|
title: "Invitación al catálogo",
|
|
@@ -114618,6 +114970,156 @@ var skillsProfileSkillsBox$1 = {
|
|
|
114618
114970
|
desired: "Deseadas",
|
|
114619
114971
|
noDesiredSkills: "Todavía no tienes habilidades deseadas."
|
|
114620
114972
|
};
|
|
114973
|
+
var spendCapUsage$1 = {
|
|
114974
|
+
scopeTenant: "Espacio de trabajo",
|
|
114975
|
+
scopeAgent: "Este agente",
|
|
114976
|
+
scopeUserAgent: "Tu uso en este agente",
|
|
114977
|
+
intervalDay: "hoy",
|
|
114978
|
+
intervalWeek: "esta semana",
|
|
114979
|
+
intervalMonth: "este mes",
|
|
114980
|
+
intervalYear: "este año",
|
|
114981
|
+
percentUsed: "{percent}% usado",
|
|
114982
|
+
blockedBanner: "Se ha alcanzado el límite de uso. Los nuevos chats y el entrenamiento quedan en pausa hasta que el periodo se reinicie.",
|
|
114983
|
+
warningBanner: "El uso se acerca a su límite."
|
|
114984
|
+
};
|
|
114985
|
+
var spendCapsTabLabels$1 = {
|
|
114986
|
+
header: {
|
|
114987
|
+
title: "Facturación",
|
|
114988
|
+
description: "Define límites de gasto para este agente y sus usuarios, y consulta cuánto se ha usado."
|
|
114989
|
+
},
|
|
114990
|
+
capability: {
|
|
114991
|
+
title: "Límite de gasto",
|
|
114992
|
+
description: "Pon un techo al gasto en IA. Mientras este límite esté activado, todo lo que cubre cuenta para el importe de abajo; y cuando se alcanza, tú decides si se bloquean las nuevas solicitudes o simplemente se avisa a los administradores.",
|
|
114993
|
+
offHint: "Actívalo para empezar a hacer seguimiento y aplicar este límite. Tu configuración se conserva si luego lo desactivas."
|
|
114994
|
+
},
|
|
114995
|
+
subTabs: {
|
|
114996
|
+
agent: "Este agente",
|
|
114997
|
+
users: "Por usuario"
|
|
114998
|
+
},
|
|
114999
|
+
tenantSections: {
|
|
115000
|
+
workspace: {
|
|
115001
|
+
title: "Límite de gasto del espacio de trabajo",
|
|
115002
|
+
description: "Un único límite que cubre todo lo que gastan los agentes y usuarios de este espacio de trabajo. Los límites por agente y por usuario se gestionan en la configuración de cada agente, en Facturación."
|
|
115003
|
+
},
|
|
115004
|
+
noCapStats: {
|
|
115005
|
+
spentThisMonth: "Gastado este mes",
|
|
115006
|
+
spentAllTime: "Gastado en total",
|
|
115007
|
+
hint: "En todos los agentes y usuarios de este espacio de trabajo"
|
|
115008
|
+
},
|
|
115009
|
+
agents: {
|
|
115010
|
+
title: "Límites de gasto por agente",
|
|
115011
|
+
description: "Agentes con un límite de gasto configurado en este espacio de trabajo. Abre uno para gestionar su límite y sus límites por usuario.",
|
|
115012
|
+
filter: {
|
|
115013
|
+
label: "Buscar agentes",
|
|
115014
|
+
placeholder: "Buscar agentes…",
|
|
115015
|
+
typeToSearch: "Escribe al menos 2 caracteres para buscar",
|
|
115016
|
+
searching: "Buscando agentes…",
|
|
115017
|
+
noResults: "No se encontraron agentes",
|
|
115018
|
+
clear: "Quitar el filtro de agente"
|
|
115019
|
+
},
|
|
115020
|
+
table: {
|
|
115021
|
+
agentColumn: "Agente",
|
|
115022
|
+
limitColumn: "Límite",
|
|
115023
|
+
spentColumn: "Gastado",
|
|
115024
|
+
remainingColumn: "Restante",
|
|
115025
|
+
statusColumn: "Estado"
|
|
115026
|
+
},
|
|
115027
|
+
emptyState: "Aún no hay límites de gasto por agente. Busca un agente arriba para configurar uno.",
|
|
115028
|
+
emptyFiltered: "Este agente aún no tiene límite de gasto.",
|
|
115029
|
+
setLimitButton: "Configurar límite de gasto",
|
|
115030
|
+
manageAria: "Gestionar los límites de gasto de {name}",
|
|
115031
|
+
statusToggleAria: "Activar o desactivar el límite de gasto de {name}"
|
|
115032
|
+
}
|
|
115033
|
+
},
|
|
115034
|
+
form: {
|
|
115035
|
+
limitLabel: "Límite de gasto (USD)",
|
|
115036
|
+
limitPlaceholder: "p. ej. 250.00",
|
|
115037
|
+
limitError: "Introduce un importe mayor que 0",
|
|
115038
|
+
intervalLabel: "Se reinicia cada",
|
|
115039
|
+
intervalOptions: {
|
|
115040
|
+
day: "Día",
|
|
115041
|
+
week: "Semana",
|
|
115042
|
+
month: "Mes",
|
|
115043
|
+
year: "Año"
|
|
115044
|
+
},
|
|
115045
|
+
enforcementLabel: "Al alcanzar el límite",
|
|
115046
|
+
enforcementOptions: {
|
|
115047
|
+
block: "Bloquear solicitudes",
|
|
115048
|
+
alertOnly: "Solo alertar"
|
|
115049
|
+
},
|
|
115050
|
+
enforcementHelp: {
|
|
115051
|
+
block: "Las nuevas solicitudes de chat y entrenamiento se rechazan hasta que el periodo se reinicie.",
|
|
115052
|
+
alertOnly: "No se bloquea nada: se notifica a los administradores cuando el gasto se acerca al límite."
|
|
115053
|
+
},
|
|
115054
|
+
thresholdsLabel: "Alertar al (% del límite)",
|
|
115055
|
+
thresholdsPlaceholder: "p. ej. 80, 95",
|
|
115056
|
+
thresholdsHelp: "Porcentajes separados por comas en los que los administradores reciben una alerta de proximidad al límite.",
|
|
115057
|
+
thresholdsError: "Usa números separados por comas entre 1 y 100",
|
|
115058
|
+
noCapHint: "Aún no hay límite de gasto configurado: define un importe y guarda para crearlo.",
|
|
115059
|
+
save: "Guardar",
|
|
115060
|
+
saving: "Guardando…",
|
|
115061
|
+
remove: "Eliminar límite",
|
|
115062
|
+
removing: "Eliminando…",
|
|
115063
|
+
cancel: "Cancelar"
|
|
115064
|
+
},
|
|
115065
|
+
usage: {
|
|
115066
|
+
title: "Uso del límite de gasto",
|
|
115067
|
+
periodTitles: {
|
|
115068
|
+
day: "Uso diario",
|
|
115069
|
+
week: "Uso semanal",
|
|
115070
|
+
month: "Uso mensual",
|
|
115071
|
+
year: "Uso anual"
|
|
115072
|
+
},
|
|
115073
|
+
spent: "Gastado: {amount}",
|
|
115074
|
+
remaining: "Restante: {amount}",
|
|
115075
|
+
percentUsed: "{percent}% del límite usado",
|
|
115076
|
+
exceededBadge: "Límite alcanzado",
|
|
115077
|
+
alertOnlyBadge: "Solo alerta",
|
|
115078
|
+
disabledBadge: "Desactivado"
|
|
115079
|
+
},
|
|
115080
|
+
users: {
|
|
115081
|
+
sectionTitle: "Límites por usuario",
|
|
115082
|
+
sectionDescription: "Limita cuánto pueden gastar usuarios concretos en este agente.",
|
|
115083
|
+
addButton: "Añadir límite de usuario",
|
|
115084
|
+
addTitle: "Nuevo límite de usuario",
|
|
115085
|
+
editTitle: "Editar límite de usuario",
|
|
115086
|
+
cancel: "Cancelar",
|
|
115087
|
+
emptyState: "Aún no hay límites por usuario. Añade uno para limitar a un usuario concreto en este agente.",
|
|
115088
|
+
edit: "Editar",
|
|
115089
|
+
"delete": "Eliminar",
|
|
115090
|
+
table: {
|
|
115091
|
+
userColumn: "Usuario",
|
|
115092
|
+
limitColumn: "Límite",
|
|
115093
|
+
statusColumn: "Estado"
|
|
115094
|
+
},
|
|
115095
|
+
actionsAria: "Acciones para {username}",
|
|
115096
|
+
statusToggleAria: "Activar o desactivar el límite de gasto de {username}",
|
|
115097
|
+
picker: {
|
|
115098
|
+
label: "Usuario",
|
|
115099
|
+
placeholder: "Buscar usuarios…",
|
|
115100
|
+
typeToSearch: "Escribe al menos 2 caracteres para buscar",
|
|
115101
|
+
searching: "Buscando usuarios…",
|
|
115102
|
+
noResults: "No se encontraron usuarios",
|
|
115103
|
+
clear: "Cambiar de usuario"
|
|
115104
|
+
}
|
|
115105
|
+
},
|
|
115106
|
+
denied: {
|
|
115107
|
+
title: "Sin acceso a los límites de gasto",
|
|
115108
|
+
description: "No tienes permiso para ver los límites de gasto aquí. Pide acceso a un administrador."
|
|
115109
|
+
},
|
|
115110
|
+
deleteModal: {
|
|
115111
|
+
title: "Eliminar límite de gasto",
|
|
115112
|
+
description: "El gasto dejará de estar limitado y de contabilizarse contra este límite.",
|
|
115113
|
+
cancel: "Cancelar",
|
|
115114
|
+
confirm: "Eliminar"
|
|
115115
|
+
},
|
|
115116
|
+
toasts: {
|
|
115117
|
+
saved: "Límite de gasto guardado",
|
|
115118
|
+
saveError: "No se pudo guardar el límite de gasto",
|
|
115119
|
+
deleted: "Límite de gasto eliminado",
|
|
115120
|
+
deleteError: "No se pudo eliminar el límite de gasto"
|
|
115121
|
+
}
|
|
115122
|
+
};
|
|
114621
115123
|
var tabsAlertstab$1 = {
|
|
114622
115124
|
searchAlertsPlaceholder: "Buscar alertas",
|
|
114623
115125
|
searchAlertsAriaLabel: "Buscar alertas",
|
|
@@ -116480,6 +116982,8 @@ var es = {
|
|
|
116480
116982
|
skillsProfileResumeBox: skillsProfileResumeBox$1,
|
|
116481
116983
|
skillsProfileSkillDetailModal: skillsProfileSkillDetailModal$1,
|
|
116482
116984
|
skillsProfileSkillsBox: skillsProfileSkillsBox$1,
|
|
116985
|
+
spendCapUsage: spendCapUsage$1,
|
|
116986
|
+
spendCapsTabLabels: spendCapsTabLabels$1,
|
|
116483
116987
|
tabsAlertstab: tabsAlertstab$1,
|
|
116484
116988
|
tabsDisclaimersTab: tabsDisclaimersTab$1,
|
|
116485
116989
|
tabsEmbedTab: tabsEmbedTab$1,
|
|
@@ -118209,7 +118713,13 @@ var chatIndex = {
|
|
|
118209
118713
|
confirmScreenShareTitle: "确认屏幕共享",
|
|
118210
118714
|
confirmScreenShareDescription: "您要与您的助手开始屏幕共享吗?",
|
|
118211
118715
|
cancel: "取消",
|
|
118212
|
-
confirm: "确认"
|
|
118716
|
+
confirm: "确认",
|
|
118717
|
+
spendCapLimitDay: "您已达到此助手今天的使用限额。",
|
|
118718
|
+
spendCapLimitWeek: "您已达到此助手本周的使用限额。",
|
|
118719
|
+
spendCapLimitMonth: "您已达到此助手本月的使用限额。",
|
|
118720
|
+
spendCapLimitYear: "您已达到此助手今年的使用限额。",
|
|
118721
|
+
spendCapLimitGeneric: "您已达到此助手的使用限额。",
|
|
118722
|
+
spendCapContactAdmin: "请在限额重置后重试,或联系您的管理员。"
|
|
118213
118723
|
};
|
|
118214
118724
|
var chatInputForm = {
|
|
118215
118725
|
uploadingOneFile: "正在上传 1 个文件...",
|
|
@@ -118412,6 +118922,8 @@ var clawSandboxAgentSkills = {
|
|
|
118412
118922
|
fieldCategoryPlaceholder: "例如 web、代码、数据",
|
|
118413
118923
|
privateToAgent: "仅此智能体",
|
|
118414
118924
|
privateToAgentHint: "私有技能仅对此智能体可用,并优先于具有相同 slug 的平台技能。",
|
|
118925
|
+
privateToOtherAgentHint: "该技能为其他智能体私有,因此无法在此更改该设置。",
|
|
118926
|
+
privateToOtherAgentTooltip: "该技能为其他智能体私有,无法在此添加。",
|
|
118415
118927
|
instructionHint: "使用该技能时智能体遵循的操作指南。修改会立即生效,包括正在进行的对话。",
|
|
118416
118928
|
filesHint: "智能体在使用此技能时可参考的可选资源。参考资料和脚本是文本文件;资产是上传的文件。",
|
|
118417
118929
|
filesAfterCreateHint: "创建技能后即可附加资源(通过“编辑”重新打开)。",
|
|
@@ -120229,7 +120741,8 @@ var profileBilling = {
|
|
|
120229
120741
|
addCredits: "添加积分",
|
|
120230
120742
|
manageBilling: "管理账单",
|
|
120231
120743
|
availableLabel: "可用",
|
|
120232
|
-
|
|
120744
|
+
creditsUsdEquivalent: "约合 {amount}",
|
|
120745
|
+
creditsConversionHint: "{credits} 积分 = $1",
|
|
120233
120746
|
creditsUnit: "{amount} 积分",
|
|
120234
120747
|
consumedLabel: "已消耗",
|
|
120235
120748
|
usedThisPeriod: "本期已使用",
|
|
@@ -120241,10 +120754,17 @@ var profileBilling = {
|
|
|
120241
120754
|
disabled: "已禁用",
|
|
120242
120755
|
manageUsage: "管理用量",
|
|
120243
120756
|
thresholdLabel: "阈值",
|
|
120757
|
+
thresholdHint: "当可用积分低于此金额时,将自动开始充值",
|
|
120244
120758
|
rechargeAmountLabel: "充值金额",
|
|
120759
|
+
rechargeAmountHint: "每次充值时从您的付款方式扣款,并添加到您的积分中",
|
|
120245
120760
|
spendingLimitLabel: "消费限额",
|
|
120761
|
+
spendingLimitHint: "每个账单周期内自动充值可扣款的上限",
|
|
120246
120762
|
unlimited: "无限制",
|
|
120247
|
-
autoRechargeDisabledMessage: "自动充值当前已禁用。启用后可在不中断服务的情况下保持余额充足。"
|
|
120763
|
+
autoRechargeDisabledMessage: "自动充值当前已禁用。启用后可在不中断服务的情况下保持余额充足。",
|
|
120764
|
+
billingTabsAriaLabel: "账单板块",
|
|
120765
|
+
tabPlanAndCredits: "套餐与积分",
|
|
120766
|
+
tabSpendLimits: "支出限额",
|
|
120767
|
+
tabAgentLimits: "智能体限额"
|
|
120248
120768
|
};
|
|
120249
120769
|
var profileCatalogInviteModal = {
|
|
120250
120770
|
title: "目录邀请",
|
|
@@ -121188,6 +121708,156 @@ var skillsProfileSkillsBox = {
|
|
|
121188
121708
|
desired: "期望",
|
|
121189
121709
|
noDesiredSkills: "您还没有任何期望的技能。"
|
|
121190
121710
|
};
|
|
121711
|
+
var spendCapUsage = {
|
|
121712
|
+
scopeTenant: "工作区",
|
|
121713
|
+
scopeAgent: "此智能体",
|
|
121714
|
+
scopeUserAgent: "您在此智能体上的用量",
|
|
121715
|
+
intervalDay: "今天",
|
|
121716
|
+
intervalWeek: "本周",
|
|
121717
|
+
intervalMonth: "本月",
|
|
121718
|
+
intervalYear: "今年",
|
|
121719
|
+
percentUsed: "已使用 {percent}%",
|
|
121720
|
+
blockedBanner: "已达到使用限额。在周期重置前,新的聊天和训练将暂停。",
|
|
121721
|
+
warningBanner: "用量即将达到限额。"
|
|
121722
|
+
};
|
|
121723
|
+
var spendCapsTabLabels = {
|
|
121724
|
+
header: {
|
|
121725
|
+
title: "账单",
|
|
121726
|
+
description: "为此智能体及其用户设置支出限额,并查看已用金额。"
|
|
121727
|
+
},
|
|
121728
|
+
capability: {
|
|
121729
|
+
title: "支出限额",
|
|
121730
|
+
description: "为 AI 支出设置上限。此限额开启期间,其覆盖的所有支出都会计入下方金额;达到限额后,由您决定是阻止新请求,还是仅提醒管理员。",
|
|
121731
|
+
offHint: "开启后即开始统计并执行此限额。之后关闭也会保留您在此的配置。"
|
|
121732
|
+
},
|
|
121733
|
+
subTabs: {
|
|
121734
|
+
agent: "此智能体",
|
|
121735
|
+
users: "按用户"
|
|
121736
|
+
},
|
|
121737
|
+
tenantSections: {
|
|
121738
|
+
workspace: {
|
|
121739
|
+
title: "工作区支出限额",
|
|
121740
|
+
description: "一个覆盖此工作区内所有智能体和用户全部支出的限额。按智能体和按用户的限额请在各智能体设置的“账单”中管理。"
|
|
121741
|
+
},
|
|
121742
|
+
noCapStats: {
|
|
121743
|
+
spentThisMonth: "本月已支出",
|
|
121744
|
+
spentAllTime: "累计已支出",
|
|
121745
|
+
hint: "涵盖此工作区内的所有智能体和用户"
|
|
121746
|
+
},
|
|
121747
|
+
agents: {
|
|
121748
|
+
title: "智能体支出限额",
|
|
121749
|
+
description: "此工作区中已配置支出限额的智能体。打开任意一个即可管理其限额及按用户限额。",
|
|
121750
|
+
filter: {
|
|
121751
|
+
label: "搜索智能体",
|
|
121752
|
+
placeholder: "搜索智能体…",
|
|
121753
|
+
typeToSearch: "请输入至少 2 个字符进行搜索",
|
|
121754
|
+
searching: "正在搜索智能体…",
|
|
121755
|
+
noResults: "未找到智能体",
|
|
121756
|
+
clear: "清除智能体筛选"
|
|
121757
|
+
},
|
|
121758
|
+
table: {
|
|
121759
|
+
agentColumn: "智能体",
|
|
121760
|
+
limitColumn: "限额",
|
|
121761
|
+
spentColumn: "已支出",
|
|
121762
|
+
remainingColumn: "剩余",
|
|
121763
|
+
statusColumn: "状态"
|
|
121764
|
+
},
|
|
121765
|
+
emptyState: "尚无智能体支出限额。请在上方搜索智能体进行设置。",
|
|
121766
|
+
emptyFiltered: "此智能体尚未设置支出限额。",
|
|
121767
|
+
setLimitButton: "设置支出限额",
|
|
121768
|
+
manageAria: "管理 {name} 的支出限额",
|
|
121769
|
+
statusToggleAria: "开启或关闭 {name} 的支出限额"
|
|
121770
|
+
}
|
|
121771
|
+
},
|
|
121772
|
+
form: {
|
|
121773
|
+
limitLabel: "支出限额(美元)",
|
|
121774
|
+
limitPlaceholder: "例如 250.00",
|
|
121775
|
+
limitError: "请输入大于 0 的金额",
|
|
121776
|
+
intervalLabel: "重置周期",
|
|
121777
|
+
intervalOptions: {
|
|
121778
|
+
day: "每天",
|
|
121779
|
+
week: "每周",
|
|
121780
|
+
month: "每月",
|
|
121781
|
+
year: "每年"
|
|
121782
|
+
},
|
|
121783
|
+
enforcementLabel: "达到限额时",
|
|
121784
|
+
enforcementOptions: {
|
|
121785
|
+
block: "阻止请求",
|
|
121786
|
+
alertOnly: "仅提醒"
|
|
121787
|
+
},
|
|
121788
|
+
enforcementHelp: {
|
|
121789
|
+
block: "在周期重置前,新的聊天和训练请求将被拒绝。",
|
|
121790
|
+
alertOnly: "不会阻止任何请求——当支出接近限额时通知管理员。"
|
|
121791
|
+
},
|
|
121792
|
+
thresholdsLabel: "提醒阈值(限额百分比)",
|
|
121793
|
+
thresholdsPlaceholder: "例如 80, 95",
|
|
121794
|
+
thresholdsHelp: "以逗号分隔的百分比,达到时管理员会收到接近限额的提醒。",
|
|
121795
|
+
thresholdsError: "请使用 1 到 100 之间、以逗号分隔的数字",
|
|
121796
|
+
noCapHint: "尚未配置支出限额——设置金额并保存即可创建。",
|
|
121797
|
+
save: "保存",
|
|
121798
|
+
saving: "保存中…",
|
|
121799
|
+
remove: "删除限额",
|
|
121800
|
+
removing: "删除中…",
|
|
121801
|
+
cancel: "取消"
|
|
121802
|
+
},
|
|
121803
|
+
usage: {
|
|
121804
|
+
title: "支出限额使用情况",
|
|
121805
|
+
periodTitles: {
|
|
121806
|
+
day: "每日用量",
|
|
121807
|
+
week: "每周用量",
|
|
121808
|
+
month: "每月用量",
|
|
121809
|
+
year: "每年用量"
|
|
121810
|
+
},
|
|
121811
|
+
spent: "已用:{amount}",
|
|
121812
|
+
remaining: "剩余:{amount}",
|
|
121813
|
+
percentUsed: "已使用限额的 {percent}%",
|
|
121814
|
+
exceededBadge: "已达限额",
|
|
121815
|
+
alertOnlyBadge: "仅提醒",
|
|
121816
|
+
disabledBadge: "已关闭"
|
|
121817
|
+
},
|
|
121818
|
+
users: {
|
|
121819
|
+
sectionTitle: "按用户限额",
|
|
121820
|
+
sectionDescription: "限制特定用户在此智能体上的支出。",
|
|
121821
|
+
addButton: "添加用户限额",
|
|
121822
|
+
addTitle: "新建用户限额",
|
|
121823
|
+
editTitle: "编辑用户限额",
|
|
121824
|
+
cancel: "取消",
|
|
121825
|
+
emptyState: "尚无按用户设置的限额。添加一个以限制此智能体上的特定用户。",
|
|
121826
|
+
edit: "编辑",
|
|
121827
|
+
"delete": "删除",
|
|
121828
|
+
table: {
|
|
121829
|
+
userColumn: "用户",
|
|
121830
|
+
limitColumn: "限额",
|
|
121831
|
+
statusColumn: "状态"
|
|
121832
|
+
},
|
|
121833
|
+
actionsAria: "{username} 的操作",
|
|
121834
|
+
statusToggleAria: "开启或关闭 {username} 的支出限额",
|
|
121835
|
+
picker: {
|
|
121836
|
+
label: "用户",
|
|
121837
|
+
placeholder: "搜索用户…",
|
|
121838
|
+
typeToSearch: "请输入至少 2 个字符进行搜索",
|
|
121839
|
+
searching: "正在搜索用户…",
|
|
121840
|
+
noResults: "未找到用户",
|
|
121841
|
+
clear: "更换用户"
|
|
121842
|
+
}
|
|
121843
|
+
},
|
|
121844
|
+
denied: {
|
|
121845
|
+
title: "无权访问支出限额",
|
|
121846
|
+
description: "您没有权限在此查看支出限额。请向管理员申请访问权限。"
|
|
121847
|
+
},
|
|
121848
|
+
deleteModal: {
|
|
121849
|
+
title: "删除支出限额",
|
|
121850
|
+
description: "支出将不再受此限额限制或统计。",
|
|
121851
|
+
cancel: "取消",
|
|
121852
|
+
confirm: "删除"
|
|
121853
|
+
},
|
|
121854
|
+
toasts: {
|
|
121855
|
+
saved: "支出限额已保存",
|
|
121856
|
+
saveError: "无法保存支出限额",
|
|
121857
|
+
deleted: "支出限额已删除",
|
|
121858
|
+
deleteError: "无法删除支出限额"
|
|
121859
|
+
}
|
|
121860
|
+
};
|
|
121191
121861
|
var tabsAlertstab = {
|
|
121192
121862
|
searchAlertsPlaceholder: "搜索提醒",
|
|
121193
121863
|
searchAlertsAriaLabel: "搜索提醒",
|
|
@@ -123050,6 +123720,8 @@ var zh = {
|
|
|
123050
123720
|
skillsProfileResumeBox: skillsProfileResumeBox,
|
|
123051
123721
|
skillsProfileSkillDetailModal: skillsProfileSkillDetailModal,
|
|
123052
123722
|
skillsProfileSkillsBox: skillsProfileSkillsBox,
|
|
123723
|
+
spendCapUsage: spendCapUsage,
|
|
123724
|
+
spendCapsTabLabels: spendCapsTabLabels,
|
|
123053
123725
|
tabsAlertstab: tabsAlertstab,
|
|
123054
123726
|
tabsDisclaimersTab: tabsDisclaimersTab,
|
|
123055
123727
|
tabsEmbedTab: tabsEmbedTab,
|
|
@@ -188711,6 +189383,1002 @@ function AddCreditsModal({ isOpen, onClose, tenant }) {
|
|
|
188711
189383
|
return (jsx(Dialog, { open: isOpen, onOpenChange: handleClose, children: jsxs(DialogContent, { className: "max-w-md", children: [jsxs(DialogHeader, { children: [jsx(DialogTitle, { children: t('profileAddCreditsModal.title') }), jsx(DialogDescription, { children: t('profileAddCreditsModal.description') })] }), jsxs("form", { onSubmit: handleSubmit, className: "space-y-6", children: [jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "credit-amount", className: "text-sm font-medium", children: t('profileAddCreditsModal.amountLabel') }), jsxs("div", { className: "relative", children: [jsx(DollarSign, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-slate-400" }), jsx(Input, { id: "credit-amount", type: "number", min: "0.01", step: "0.01", placeholder: "50.00", value: amount, onChange: (e) => setAmount(e.target.value), className: "pl-9" })] }), jsx("p", { className: "text-xs text-slate-500 dark:text-slate-400", children: t('profileAddCreditsModal.amountHint') })] }), jsxs("div", { className: "flex items-start gap-2 p-3 bg-blue-50 dark:bg-blue-900/20 rounded-lg border border-blue-100 dark:border-blue-800", children: [jsx(CircleAlert, { className: "h-4 w-4 text-blue-500 mt-0.5 flex-shrink-0" }), jsx("p", { className: "text-xs text-blue-700 dark:text-blue-300", children: t('profileAddCreditsModal.paymentMethodNotice') })] }), jsxs(DialogFooter, { className: "gap-2 sm:gap-0", children: [jsx(Button$1, { type: "button", variant: "outline", onClick: handleClose, children: t('profileAddCreditsModal.cancel') }), jsx(Button$1, { type: "submit", disabled: isLoading, className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] hover:opacity-90 text-white", children: isLoading ? (jsxs(Fragment$1, { children: [jsx(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), t('profileAddCreditsModal.processing')] })) : (t('profileAddCreditsModal.addCredits')) })] })] })] }) }));
|
|
188712
189384
|
}
|
|
188713
189385
|
|
|
189386
|
+
/**
|
|
189387
|
+
* The info icon that sits on every analytics card and, on hover or keyboard
|
|
189388
|
+
* focus, explains what the card measures. The description doubles as the
|
|
189389
|
+
* accessible name so screen readers get it without needing the hover.
|
|
189390
|
+
*
|
|
189391
|
+
* Uses the tooltip primitives directly rather than the shared `TooltipContent`
|
|
189392
|
+
* because analytics wants a light popover, while `TooltipContent` is the dark
|
|
189393
|
+
* `bg-primary` style the rest of the app relies on.
|
|
189394
|
+
*/
|
|
189395
|
+
function CardInfoIcon({ title, description, className }) {
|
|
189396
|
+
return (jsxs(Tooltip$1, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx("button", { type: "button", "aria-label": description, className: cn('shrink-0 cursor-help rounded p-1 text-[#898989] transition-colors hover:bg-gray-100 hover:text-[#38A1E5] focus-visible:ring-2 focus-visible:ring-[#38A1E5] focus-visible:outline-none', className), children: jsx(Info$3, { className: "h-4 w-4", "aria-hidden": "true" }) }) }), jsx(Portal$2, { children: jsxs(Content2$3, { "data-slot": "tooltip-content", side: "top", align: "center", sideOffset: 6, collisionPadding: 12, className: "animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 z-50 max-w-[260px] rounded-lg border border-[oklch(0.922_0_0)] bg-white p-3 shadow-lg", children: [title && (jsx("div", { "data-slot": "tooltip-title", className: "mb-1 text-sm font-semibold text-[#484848]", children: title })), jsx("p", { "data-slot": "tooltip-description", className: "text-sm leading-relaxed text-[#646464]", children: description })] }) })] }));
|
|
189397
|
+
}
|
|
189398
|
+
|
|
189399
|
+
/**
|
|
189400
|
+
* Shared utilities for tab label systems.
|
|
189401
|
+
*
|
|
189402
|
+
* Every tab exports a `resolve*Labels(override?)` function that deep-merges
|
|
189403
|
+
* a partial override into the default AGENT labels. This file provides the
|
|
189404
|
+
* DeepPartial type and deepMerge implementation they all share.
|
|
189405
|
+
*/
|
|
189406
|
+
function deepMerge(base, override) {
|
|
189407
|
+
const result = { ...base };
|
|
189408
|
+
const overrideBag = override;
|
|
189409
|
+
for (const key of Object.keys(overrideBag)) {
|
|
189410
|
+
const overrideValue = overrideBag[key];
|
|
189411
|
+
if (overrideValue === undefined)
|
|
189412
|
+
continue;
|
|
189413
|
+
const baseValue = base[key];
|
|
189414
|
+
if (baseValue &&
|
|
189415
|
+
typeof baseValue === 'object' &&
|
|
189416
|
+
!Array.isArray(baseValue) &&
|
|
189417
|
+
overrideValue &&
|
|
189418
|
+
typeof overrideValue === 'object' &&
|
|
189419
|
+
!Array.isArray(overrideValue)) {
|
|
189420
|
+
result[key] = deepMerge(baseValue, overrideValue);
|
|
189421
|
+
}
|
|
189422
|
+
else {
|
|
189423
|
+
result[key] = overrideValue;
|
|
189424
|
+
}
|
|
189425
|
+
}
|
|
189426
|
+
return result;
|
|
189427
|
+
}
|
|
189428
|
+
|
|
189429
|
+
const AGENT_SPEND_CAPS_TAB_LABELS = {
|
|
189430
|
+
header: {
|
|
189431
|
+
title: 'Billing',
|
|
189432
|
+
description: 'Set spend limits for this agent and its users, and see how much has been used.',
|
|
189433
|
+
},
|
|
189434
|
+
capability: {
|
|
189435
|
+
title: 'Spend Limit',
|
|
189436
|
+
description: 'Put a ceiling on AI spend. While this limit is on, everything it covers counts toward the amount below — and when the limit is reached, you choose whether new requests are blocked or admins are simply alerted.',
|
|
189437
|
+
offHint: 'Turn it on to start tracking and enforcing this limit. Your setup here is kept safe if you later turn it off.',
|
|
189438
|
+
},
|
|
189439
|
+
subTabs: {
|
|
189440
|
+
agent: 'This Agent',
|
|
189441
|
+
users: 'Per User',
|
|
189442
|
+
},
|
|
189443
|
+
tenantSections: {
|
|
189444
|
+
workspace: {
|
|
189445
|
+
title: 'Workspace Spend Limit',
|
|
189446
|
+
description: 'One limit covering everything all agents and users spend in this workspace. Per-agent and per-user limits live in each agent’s settings under Billing.',
|
|
189447
|
+
},
|
|
189448
|
+
noCapStats: {
|
|
189449
|
+
spentThisMonth: 'Spent This Month',
|
|
189450
|
+
spentAllTime: 'Spent All Time',
|
|
189451
|
+
hint: 'Across all agents and users in this workspace',
|
|
189452
|
+
},
|
|
189453
|
+
agents: {
|
|
189454
|
+
title: 'Agent Spend Limits',
|
|
189455
|
+
description: 'Agents with a spend limit configured in this workspace. Open one to manage its limit and its per-user limits.',
|
|
189456
|
+
filter: {
|
|
189457
|
+
label: 'Search Agents',
|
|
189458
|
+
placeholder: 'Search Agents…',
|
|
189459
|
+
typeToSearch: 'Type at least 2 characters to search',
|
|
189460
|
+
searching: 'Searching agents…',
|
|
189461
|
+
noResults: 'No agents found',
|
|
189462
|
+
clear: 'Clear Agent Filter',
|
|
189463
|
+
},
|
|
189464
|
+
table: {
|
|
189465
|
+
agentColumn: 'Agent',
|
|
189466
|
+
limitColumn: 'Limit',
|
|
189467
|
+
spentColumn: 'Spent',
|
|
189468
|
+
remainingColumn: 'Remaining',
|
|
189469
|
+
statusColumn: 'Status',
|
|
189470
|
+
},
|
|
189471
|
+
emptyState: 'No agent spend limits yet. Search for an agent above to set one.',
|
|
189472
|
+
emptyFiltered: 'No spend limit for this agent yet.',
|
|
189473
|
+
setLimitButton: 'Set Spend Limit',
|
|
189474
|
+
manageAria: (name) => `Manage spend limits for ${name}`,
|
|
189475
|
+
statusToggleAria: (name) => `Toggle spend limit for ${name}`,
|
|
189476
|
+
},
|
|
189477
|
+
},
|
|
189478
|
+
form: {
|
|
189479
|
+
limitLabel: 'Spend Limit (USD)',
|
|
189480
|
+
limitPlaceholder: 'e.g. 250.00',
|
|
189481
|
+
limitError: 'Enter an amount greater than 0',
|
|
189482
|
+
intervalLabel: 'Resets Every',
|
|
189483
|
+
intervalOptions: {
|
|
189484
|
+
day: 'Day',
|
|
189485
|
+
week: 'Week',
|
|
189486
|
+
month: 'Month',
|
|
189487
|
+
year: 'Year',
|
|
189488
|
+
},
|
|
189489
|
+
enforcementLabel: 'When the Limit Is Reached',
|
|
189490
|
+
enforcementOptions: {
|
|
189491
|
+
block: 'Block Requests',
|
|
189492
|
+
alertOnly: 'Alert Only',
|
|
189493
|
+
},
|
|
189494
|
+
enforcementHelp: {
|
|
189495
|
+
block: 'New chat and training requests are refused until the period resets.',
|
|
189496
|
+
alertOnly: 'Nothing is blocked — admins are notified when spend nears the limit.',
|
|
189497
|
+
},
|
|
189498
|
+
thresholdsLabel: 'Alert At (% of Limit)',
|
|
189499
|
+
thresholdsPlaceholder: 'e.g. 80, 95',
|
|
189500
|
+
thresholdsHelp: 'Comma-separated percentages at which admins get a near-limit alert.',
|
|
189501
|
+
thresholdsError: 'Use comma-separated numbers between 1 and 100',
|
|
189502
|
+
noCapHint: 'No spend limit configured yet — set an amount and save to create one.',
|
|
189503
|
+
save: 'Save',
|
|
189504
|
+
saving: 'Saving…',
|
|
189505
|
+
cancel: 'Cancel',
|
|
189506
|
+
remove: 'Delete Limit',
|
|
189507
|
+
removing: 'Deleting…',
|
|
189508
|
+
},
|
|
189509
|
+
usage: {
|
|
189510
|
+
title: 'Spend Limit Usage',
|
|
189511
|
+
periodTitles: {
|
|
189512
|
+
day: 'Daily Usage',
|
|
189513
|
+
week: 'Weekly Usage',
|
|
189514
|
+
month: 'Monthly Usage',
|
|
189515
|
+
year: 'Yearly Usage',
|
|
189516
|
+
},
|
|
189517
|
+
spent: 'Spent: {amount}',
|
|
189518
|
+
remaining: 'Remaining: {amount}',
|
|
189519
|
+
percentUsed: '{percent}% of limit used',
|
|
189520
|
+
exceededBadge: 'Limit Reached',
|
|
189521
|
+
alertOnlyBadge: 'Alert Only',
|
|
189522
|
+
disabledBadge: 'Off',
|
|
189523
|
+
},
|
|
189524
|
+
users: {
|
|
189525
|
+
sectionTitle: 'Per-User Limits',
|
|
189526
|
+
sectionDescription: 'Limit what specific users can spend on this agent.',
|
|
189527
|
+
addButton: 'Add User Limit',
|
|
189528
|
+
addTitle: 'New User Limit',
|
|
189529
|
+
editTitle: 'Edit User Limit',
|
|
189530
|
+
cancel: 'Cancel',
|
|
189531
|
+
emptyState: 'No per-user limits yet. Add one to limit a specific user on this agent.',
|
|
189532
|
+
edit: 'Edit',
|
|
189533
|
+
delete: 'Delete',
|
|
189534
|
+
table: {
|
|
189535
|
+
userColumn: 'User',
|
|
189536
|
+
limitColumn: 'Limit',
|
|
189537
|
+
statusColumn: 'Status',
|
|
189538
|
+
},
|
|
189539
|
+
actionsAria: (username) => `Actions for ${username}`,
|
|
189540
|
+
statusToggleAria: (username) => `Toggle spend limit for ${username}`,
|
|
189541
|
+
picker: {
|
|
189542
|
+
label: 'User',
|
|
189543
|
+
placeholder: 'Search Users…',
|
|
189544
|
+
typeToSearch: 'Type at least 2 characters to search',
|
|
189545
|
+
searching: 'Searching users…',
|
|
189546
|
+
noResults: 'No users found',
|
|
189547
|
+
clear: 'Change User',
|
|
189548
|
+
},
|
|
189549
|
+
},
|
|
189550
|
+
denied: {
|
|
189551
|
+
title: 'No Access to Spend Limits',
|
|
189552
|
+
description: "You don't have permission to view spend limits here. Ask an administrator for access.",
|
|
189553
|
+
},
|
|
189554
|
+
deleteModal: {
|
|
189555
|
+
title: 'Delete Spend Limit',
|
|
189556
|
+
description: 'Spending will no longer be limited or tracked against this limit.',
|
|
189557
|
+
cancel: 'Cancel',
|
|
189558
|
+
confirm: 'Delete',
|
|
189559
|
+
},
|
|
189560
|
+
toasts: {
|
|
189561
|
+
saved: 'Spend limit saved',
|
|
189562
|
+
saveError: "Couldn't save the spend limit",
|
|
189563
|
+
deleted: 'Spend limit deleted',
|
|
189564
|
+
deleteError: "Couldn't delete the spend limit",
|
|
189565
|
+
},
|
|
189566
|
+
};
|
|
189567
|
+
function buildSpendCapsTabLabels(t) {
|
|
189568
|
+
return {
|
|
189569
|
+
header: {
|
|
189570
|
+
title: t('spendCapsTabLabels.header.title'),
|
|
189571
|
+
description: t('spendCapsTabLabels.header.description'),
|
|
189572
|
+
},
|
|
189573
|
+
capability: {
|
|
189574
|
+
title: t('spendCapsTabLabels.capability.title'),
|
|
189575
|
+
description: t('spendCapsTabLabels.capability.description'),
|
|
189576
|
+
offHint: t('spendCapsTabLabels.capability.offHint'),
|
|
189577
|
+
},
|
|
189578
|
+
subTabs: {
|
|
189579
|
+
agent: t('spendCapsTabLabels.subTabs.agent'),
|
|
189580
|
+
users: t('spendCapsTabLabels.subTabs.users'),
|
|
189581
|
+
},
|
|
189582
|
+
tenantSections: {
|
|
189583
|
+
workspace: {
|
|
189584
|
+
title: t('spendCapsTabLabels.tenantSections.workspace.title'),
|
|
189585
|
+
description: t('spendCapsTabLabels.tenantSections.workspace.description'),
|
|
189586
|
+
},
|
|
189587
|
+
noCapStats: {
|
|
189588
|
+
spentThisMonth: t('spendCapsTabLabels.tenantSections.noCapStats.spentThisMonth'),
|
|
189589
|
+
spentAllTime: t('spendCapsTabLabels.tenantSections.noCapStats.spentAllTime'),
|
|
189590
|
+
hint: t('spendCapsTabLabels.tenantSections.noCapStats.hint'),
|
|
189591
|
+
},
|
|
189592
|
+
agents: {
|
|
189593
|
+
title: t('spendCapsTabLabels.tenantSections.agents.title'),
|
|
189594
|
+
description: t('spendCapsTabLabels.tenantSections.agents.description'),
|
|
189595
|
+
filter: {
|
|
189596
|
+
label: t('spendCapsTabLabels.tenantSections.agents.filter.label'),
|
|
189597
|
+
placeholder: t('spendCapsTabLabels.tenantSections.agents.filter.placeholder'),
|
|
189598
|
+
typeToSearch: t('spendCapsTabLabels.tenantSections.agents.filter.typeToSearch'),
|
|
189599
|
+
searching: t('spendCapsTabLabels.tenantSections.agents.filter.searching'),
|
|
189600
|
+
noResults: t('spendCapsTabLabels.tenantSections.agents.filter.noResults'),
|
|
189601
|
+
clear: t('spendCapsTabLabels.tenantSections.agents.filter.clear'),
|
|
189602
|
+
},
|
|
189603
|
+
table: {
|
|
189604
|
+
agentColumn: t('spendCapsTabLabels.tenantSections.agents.table.agentColumn'),
|
|
189605
|
+
limitColumn: t('spendCapsTabLabels.tenantSections.agents.table.limitColumn'),
|
|
189606
|
+
spentColumn: t('spendCapsTabLabels.tenantSections.agents.table.spentColumn'),
|
|
189607
|
+
remainingColumn: t('spendCapsTabLabels.tenantSections.agents.table.remainingColumn'),
|
|
189608
|
+
statusColumn: t('spendCapsTabLabels.tenantSections.agents.table.statusColumn'),
|
|
189609
|
+
},
|
|
189610
|
+
emptyState: t('spendCapsTabLabels.tenantSections.agents.emptyState'),
|
|
189611
|
+
emptyFiltered: t('spendCapsTabLabels.tenantSections.agents.emptyFiltered'),
|
|
189612
|
+
setLimitButton: t('spendCapsTabLabels.tenantSections.agents.setLimitButton'),
|
|
189613
|
+
manageAria: (name) => t('spendCapsTabLabels.tenantSections.agents.manageAria', { name }),
|
|
189614
|
+
statusToggleAria: (name) => t('spendCapsTabLabels.tenantSections.agents.statusToggleAria', { name }),
|
|
189615
|
+
},
|
|
189616
|
+
},
|
|
189617
|
+
form: {
|
|
189618
|
+
limitLabel: t('spendCapsTabLabels.form.limitLabel'),
|
|
189619
|
+
limitPlaceholder: t('spendCapsTabLabels.form.limitPlaceholder'),
|
|
189620
|
+
limitError: t('spendCapsTabLabels.form.limitError'),
|
|
189621
|
+
intervalLabel: t('spendCapsTabLabels.form.intervalLabel'),
|
|
189622
|
+
intervalOptions: {
|
|
189623
|
+
day: t('spendCapsTabLabels.form.intervalOptions.day'),
|
|
189624
|
+
week: t('spendCapsTabLabels.form.intervalOptions.week'),
|
|
189625
|
+
month: t('spendCapsTabLabels.form.intervalOptions.month'),
|
|
189626
|
+
year: t('spendCapsTabLabels.form.intervalOptions.year'),
|
|
189627
|
+
},
|
|
189628
|
+
enforcementLabel: t('spendCapsTabLabels.form.enforcementLabel'),
|
|
189629
|
+
enforcementOptions: {
|
|
189630
|
+
block: t('spendCapsTabLabels.form.enforcementOptions.block'),
|
|
189631
|
+
alertOnly: t('spendCapsTabLabels.form.enforcementOptions.alertOnly'),
|
|
189632
|
+
},
|
|
189633
|
+
enforcementHelp: {
|
|
189634
|
+
block: t('spendCapsTabLabels.form.enforcementHelp.block'),
|
|
189635
|
+
alertOnly: t('spendCapsTabLabels.form.enforcementHelp.alertOnly'),
|
|
189636
|
+
},
|
|
189637
|
+
thresholdsLabel: t('spendCapsTabLabels.form.thresholdsLabel'),
|
|
189638
|
+
thresholdsPlaceholder: t('spendCapsTabLabels.form.thresholdsPlaceholder'),
|
|
189639
|
+
thresholdsHelp: t('spendCapsTabLabels.form.thresholdsHelp'),
|
|
189640
|
+
thresholdsError: t('spendCapsTabLabels.form.thresholdsError'),
|
|
189641
|
+
noCapHint: t('spendCapsTabLabels.form.noCapHint'),
|
|
189642
|
+
save: t('spendCapsTabLabels.form.save'),
|
|
189643
|
+
saving: t('spendCapsTabLabels.form.saving'),
|
|
189644
|
+
cancel: t('spendCapsTabLabels.form.cancel'),
|
|
189645
|
+
remove: t('spendCapsTabLabels.form.remove'),
|
|
189646
|
+
removing: t('spendCapsTabLabels.form.removing'),
|
|
189647
|
+
},
|
|
189648
|
+
usage: {
|
|
189649
|
+
title: t('spendCapsTabLabels.usage.title'),
|
|
189650
|
+
periodTitles: {
|
|
189651
|
+
day: t('spendCapsTabLabels.usage.periodTitles.day'),
|
|
189652
|
+
week: t('spendCapsTabLabels.usage.periodTitles.week'),
|
|
189653
|
+
month: t('spendCapsTabLabels.usage.periodTitles.month'),
|
|
189654
|
+
year: t('spendCapsTabLabels.usage.periodTitles.year'),
|
|
189655
|
+
},
|
|
189656
|
+
spent: t('spendCapsTabLabels.usage.spent'),
|
|
189657
|
+
remaining: t('spendCapsTabLabels.usage.remaining'),
|
|
189658
|
+
percentUsed: t('spendCapsTabLabels.usage.percentUsed'),
|
|
189659
|
+
exceededBadge: t('spendCapsTabLabels.usage.exceededBadge'),
|
|
189660
|
+
alertOnlyBadge: t('spendCapsTabLabels.usage.alertOnlyBadge'),
|
|
189661
|
+
disabledBadge: t('spendCapsTabLabels.usage.disabledBadge'),
|
|
189662
|
+
},
|
|
189663
|
+
users: {
|
|
189664
|
+
sectionTitle: t('spendCapsTabLabels.users.sectionTitle'),
|
|
189665
|
+
sectionDescription: t('spendCapsTabLabels.users.sectionDescription'),
|
|
189666
|
+
addButton: t('spendCapsTabLabels.users.addButton'),
|
|
189667
|
+
addTitle: t('spendCapsTabLabels.users.addTitle'),
|
|
189668
|
+
editTitle: t('spendCapsTabLabels.users.editTitle'),
|
|
189669
|
+
cancel: t('spendCapsTabLabels.users.cancel'),
|
|
189670
|
+
emptyState: t('spendCapsTabLabels.users.emptyState'),
|
|
189671
|
+
edit: t('spendCapsTabLabels.users.edit'),
|
|
189672
|
+
delete: t('spendCapsTabLabels.users.delete'),
|
|
189673
|
+
table: {
|
|
189674
|
+
userColumn: t('spendCapsTabLabels.users.table.userColumn'),
|
|
189675
|
+
limitColumn: t('spendCapsTabLabels.users.table.limitColumn'),
|
|
189676
|
+
statusColumn: t('spendCapsTabLabels.users.table.statusColumn'),
|
|
189677
|
+
},
|
|
189678
|
+
actionsAria: (username) => t('spendCapsTabLabels.users.actionsAria', { username }),
|
|
189679
|
+
statusToggleAria: (username) => t('spendCapsTabLabels.users.statusToggleAria', { username }),
|
|
189680
|
+
picker: {
|
|
189681
|
+
label: t('spendCapsTabLabels.users.picker.label'),
|
|
189682
|
+
placeholder: t('spendCapsTabLabels.users.picker.placeholder'),
|
|
189683
|
+
typeToSearch: t('spendCapsTabLabels.users.picker.typeToSearch'),
|
|
189684
|
+
searching: t('spendCapsTabLabels.users.picker.searching'),
|
|
189685
|
+
noResults: t('spendCapsTabLabels.users.picker.noResults'),
|
|
189686
|
+
clear: t('spendCapsTabLabels.users.picker.clear'),
|
|
189687
|
+
},
|
|
189688
|
+
},
|
|
189689
|
+
denied: {
|
|
189690
|
+
title: t('spendCapsTabLabels.denied.title'),
|
|
189691
|
+
description: t('spendCapsTabLabels.denied.description'),
|
|
189692
|
+
},
|
|
189693
|
+
deleteModal: {
|
|
189694
|
+
title: t('spendCapsTabLabels.deleteModal.title'),
|
|
189695
|
+
description: t('spendCapsTabLabels.deleteModal.description'),
|
|
189696
|
+
cancel: t('spendCapsTabLabels.deleteModal.cancel'),
|
|
189697
|
+
confirm: t('spendCapsTabLabels.deleteModal.confirm'),
|
|
189698
|
+
},
|
|
189699
|
+
toasts: {
|
|
189700
|
+
saved: t('spendCapsTabLabels.toasts.saved'),
|
|
189701
|
+
saveError: t('spendCapsTabLabels.toasts.saveError'),
|
|
189702
|
+
deleted: t('spendCapsTabLabels.toasts.deleted'),
|
|
189703
|
+
deleteError: t('spendCapsTabLabels.toasts.deleteError'),
|
|
189704
|
+
},
|
|
189705
|
+
};
|
|
189706
|
+
}
|
|
189707
|
+
function resolveSpendCapsTabLabels(override, t) {
|
|
189708
|
+
const base = t ? buildSpendCapsTabLabels(t) : AGENT_SPEND_CAPS_TAB_LABELS;
|
|
189709
|
+
if (!override)
|
|
189710
|
+
return base;
|
|
189711
|
+
return deepMerge(base, override);
|
|
189712
|
+
}
|
|
189713
|
+
|
|
189714
|
+
const CustomSwitch = React.forwardRef(({ className, checked, onCheckedChange, ...props }, ref) => (jsx(Root$c, { checked: checked, onCheckedChange: onCheckedChange, className: cn('peer focus-visible:ring-ring focus-visible:ring-offset-background inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-blue-500 data-[state=unchecked]:bg-gray-200', className), ...props, ref: ref, children: jsx(Thumb, { className: cn('pointer-events-none block h-5 w-5 rounded-full bg-white shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0') }) })));
|
|
189715
|
+
CustomSwitch.displayName = Root$c.displayName;
|
|
189716
|
+
|
|
189717
|
+
/**
|
|
189718
|
+
* Reusable header used at the top of every capability-gated agent-settings
|
|
189719
|
+
* tab. It surfaces the capability's on/off toggle (previously buried in the
|
|
189720
|
+
* Settings → Capabilities list) right where the feature is configured, with a
|
|
189721
|
+
* naive-friendly explanation.
|
|
189722
|
+
*
|
|
189723
|
+
* The configuration UI below is always rendered so admins can preview what
|
|
189724
|
+
* they'll get, but it's grayed out and made inert (`pointer-events-none`,
|
|
189725
|
+
* `aria-hidden`) until the capability is switched on.
|
|
189726
|
+
*/
|
|
189727
|
+
function CapabilityGate({ title, description, enabled, onToggle, disabled = false, ariaLabel, offHint, toggleTestId, children, }) {
|
|
189728
|
+
return (jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-start justify-between gap-4 rounded-lg border border-gray-200 bg-gray-50/60 p-4", "data-testid": "capability-gate-header", children: [jsx("div", { className: "min-w-0", children: jsxs("p", { className: "text-xs leading-relaxed text-gray-600", children: [description, !enabled && offHint && (jsxs("span", { className: "font-medium text-gray-500", "data-testid": "capability-gate-off-hint", children: [' ', offHint] }))] }) }), jsx(CustomSwitch, { checked: enabled, onCheckedChange: onToggle, disabled: disabled, "aria-label": ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : title, "aria-checked": enabled, "data-testid": toggleTestId, className: "mt-0.5 flex-shrink-0" })] }), jsx("div", { className: cn('transition-opacity', !enabled && 'pointer-events-none select-none opacity-50'), "aria-hidden": !enabled, inert: !enabled, "data-testid": "capability-gate-content", "data-enabled": enabled, children: children })] }));
|
|
189729
|
+
}
|
|
189730
|
+
|
|
189731
|
+
/**
|
|
189732
|
+
* Pure helpers for the spend-caps tab. The backend stores alert thresholds as
|
|
189733
|
+
* fractions of the limit (0.8 = 80%) while the form edits them as a
|
|
189734
|
+
* comma-separated percentage list, and dollar fields travel as decimal strings.
|
|
189735
|
+
*/
|
|
189736
|
+
/** Format a backend decimal string as a dollar amount, e.g. "30.500000" → "$30.50". */
|
|
189737
|
+
function formatUsd$1(value) {
|
|
189738
|
+
const parsed = Number(value);
|
|
189739
|
+
if (value == null || value === '' || Number.isNaN(parsed))
|
|
189740
|
+
return '$0.00';
|
|
189741
|
+
return `$${parsed.toFixed(2)}`;
|
|
189742
|
+
}
|
|
189743
|
+
/** Fractions → editable percent list: [0.8, 0.95] → "80, 95". */
|
|
189744
|
+
function thresholdsToInput(thresholds) {
|
|
189745
|
+
if (!Array.isArray(thresholds))
|
|
189746
|
+
return '';
|
|
189747
|
+
return thresholds
|
|
189748
|
+
.filter((value) => typeof value === 'number' && !Number.isNaN(value))
|
|
189749
|
+
.map((value) => {
|
|
189750
|
+
const percent = value * 100;
|
|
189751
|
+
return Number.isInteger(percent) ? String(percent) : percent.toFixed(1);
|
|
189752
|
+
})
|
|
189753
|
+
.join(', ');
|
|
189754
|
+
}
|
|
189755
|
+
/**
|
|
189756
|
+
* Percent list → fractions: "80, 95" → [0.8, 0.95]. Empty input is valid and
|
|
189757
|
+
* means "keep the backend default". Returns null when any entry is not a
|
|
189758
|
+
* number in (0, 100].
|
|
189759
|
+
*/
|
|
189760
|
+
function parseThresholdsInput(input) {
|
|
189761
|
+
const trimmed = input.trim();
|
|
189762
|
+
if (trimmed === '')
|
|
189763
|
+
return [];
|
|
189764
|
+
const parts = trimmed.split(',').map((part) => part.trim());
|
|
189765
|
+
const fractions = [];
|
|
189766
|
+
for (const part of parts) {
|
|
189767
|
+
if (part === '')
|
|
189768
|
+
return null;
|
|
189769
|
+
const percent = Number(part);
|
|
189770
|
+
if (Number.isNaN(percent) || percent <= 0 || percent > 100)
|
|
189771
|
+
return null;
|
|
189772
|
+
fractions.push(percent / 100);
|
|
189773
|
+
}
|
|
189774
|
+
return fractions;
|
|
189775
|
+
}
|
|
189776
|
+
/** A valid limit is any number strictly greater than 0. */
|
|
189777
|
+
function isValidLimit(input) {
|
|
189778
|
+
const parsed = Number(input);
|
|
189779
|
+
return input.trim() !== '' && !Number.isNaN(parsed) && parsed > 0;
|
|
189780
|
+
}
|
|
189781
|
+
/** Percent of the limit already spent, clamped to [0, 100] for progress bars. */
|
|
189782
|
+
function percentUsed(currentSpend, maxCost) {
|
|
189783
|
+
const spent = Number(currentSpend);
|
|
189784
|
+
const limit = Number(maxCost);
|
|
189785
|
+
if (Number.isNaN(spent) || Number.isNaN(limit) || limit <= 0)
|
|
189786
|
+
return 0;
|
|
189787
|
+
return Math.min(100, Math.max(0, Math.round((spent / limit) * 100)));
|
|
189788
|
+
}
|
|
189789
|
+
|
|
189790
|
+
/**
|
|
189791
|
+
* Editor for one spend cap of any scope. Counter fields
|
|
189792
|
+
* (current spend / remaining / exceeded) are read-only on the backend and
|
|
189793
|
+
* only rendered, never submitted.
|
|
189794
|
+
*/
|
|
189795
|
+
function SpendCapForm({ labels, cap, isSaving, isDeleting, onSave, onDelete, onCancel, showUsage = true, testId, }) {
|
|
189796
|
+
const [limit, setLimit] = React__default.useState('');
|
|
189797
|
+
const [interval, setInterval] = React__default.useState('month');
|
|
189798
|
+
const [enforcement, setEnforcement] = React__default.useState('block');
|
|
189799
|
+
const [thresholds, setThresholds] = React__default.useState('80, 95');
|
|
189800
|
+
const [enabled, setEnabled] = React__default.useState(true);
|
|
189801
|
+
// Rehydrate the form whenever the server copy changes.
|
|
189802
|
+
React__default.useEffect(() => {
|
|
189803
|
+
var _a, _b, _c;
|
|
189804
|
+
setLimit(cap ? String(Number(cap.max_cost_usd)) : '');
|
|
189805
|
+
setInterval((_a = cap === null || cap === void 0 ? void 0 : cap.interval_type) !== null && _a !== void 0 ? _a : 'month');
|
|
189806
|
+
setEnforcement((_b = cap === null || cap === void 0 ? void 0 : cap.enforcement) !== null && _b !== void 0 ? _b : 'block');
|
|
189807
|
+
setThresholds(cap ? thresholdsToInput(cap.alert_thresholds) : '80, 95');
|
|
189808
|
+
setEnabled((_c = cap === null || cap === void 0 ? void 0 : cap.enabled) !== null && _c !== void 0 ? _c : true);
|
|
189809
|
+
}, [cap === null || cap === void 0 ? void 0 : cap.id, cap === null || cap === void 0 ? void 0 : cap.updated_at]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
189810
|
+
const limitTouched = limit.trim() !== '';
|
|
189811
|
+
const limitValid = isValidLimit(limit);
|
|
189812
|
+
const parsedThresholds = parseThresholdsInput(thresholds);
|
|
189813
|
+
const thresholdsValid = parsedThresholds !== null;
|
|
189814
|
+
const canSave = limitValid && thresholdsValid && !isSaving && !isDeleting;
|
|
189815
|
+
const handleSave = () => {
|
|
189816
|
+
const formData = {
|
|
189817
|
+
interval_type: interval,
|
|
189818
|
+
max_cost_usd: Number(limit).toFixed(2),
|
|
189819
|
+
enforcement,
|
|
189820
|
+
enabled,
|
|
189821
|
+
};
|
|
189822
|
+
if (parsedThresholds && parsedThresholds.length > 0) {
|
|
189823
|
+
formData.alert_thresholds = parsedThresholds;
|
|
189824
|
+
}
|
|
189825
|
+
onSave(formData);
|
|
189826
|
+
};
|
|
189827
|
+
const usagePercent = cap ? percentUsed(cap.current_spend_usd, cap.max_cost_usd) : 0;
|
|
189828
|
+
const barColor = (cap === null || cap === void 0 ? void 0 : cap.is_exceeded)
|
|
189829
|
+
? 'bg-red-500'
|
|
189830
|
+
: usagePercent >= 80
|
|
189831
|
+
? 'bg-amber-500'
|
|
189832
|
+
: 'bg-blue-500';
|
|
189833
|
+
return (jsxs("div", { className: "space-y-4", "data-testid": `${testId}-form`, children: [!cap && (jsx("p", { className: "text-xs text-gray-500", "data-testid": `${testId}-no-cap-hint`, children: labels.form.noCapHint })), jsx(CapabilityGate, { title: labels.capability.title, description: labels.capability.description, offHint: labels.capability.offHint, enabled: enabled, onToggle: setEnabled, disabled: isSaving || isDeleting, ariaLabel: labels.capability.title, toggleTestId: `${testId}-enabled-switch`, children: jsxs("div", { className: "space-y-4", children: [cap && showUsage && (jsxs("div", { className: "space-y-2 rounded-lg border border-gray-200 bg-gray-50 p-3", "data-testid": `${testId}-usage`, children: [jsxs("div", { className: "flex items-center justify-between", children: [jsx("p", { className: "text-xs font-medium text-gray-700", children: labels.usage.periodTitles[cap.interval_type] }), jsxs("div", { className: "flex items-center gap-1.5", children: [!cap.enabled && (jsx("span", { className: "rounded-full bg-gray-200 px-2 py-0.5 text-[11px] font-medium text-gray-700", "data-testid": `${testId}-disabled-badge`, children: labels.usage.disabledBadge })), cap.enforcement === 'alert_only' && (jsx("span", { className: "rounded-full bg-amber-100 px-2 py-0.5 text-[11px] font-medium text-amber-800", "data-testid": `${testId}-alert-only-badge`, children: labels.usage.alertOnlyBadge })), cap.is_exceeded && (jsx("span", { className: "rounded-full bg-red-100 px-2 py-0.5 text-[11px] font-medium text-red-700", "data-testid": `${testId}-exceeded-badge`, children: labels.usage.exceededBadge }))] })] }), jsx("div", { className: "h-2 w-full overflow-hidden rounded-full bg-gray-200", role: "progressbar", "aria-valuenow": usagePercent, "aria-valuemin": 0, "aria-valuemax": 100, "aria-label": labels.usage.title, children: jsx("div", { className: `h-full rounded-full transition-all ${barColor}`, style: { width: `${usagePercent}%` } }) }), jsxs("div", { className: "flex items-center justify-between text-xs text-gray-600", children: [jsx("span", { "data-testid": `${testId}-spent`, children: labels.usage.spent.replace('{amount}', formatUsd$1(cap.current_spend_usd)) }), jsx("span", { "data-testid": `${testId}-percent`, children: labels.usage.percentUsed.replace('{percent}', String(usagePercent)) }), jsx("span", { "data-testid": `${testId}-remaining`, children: labels.usage.remaining.replace('{amount}', formatUsd$1(cap.remaining_usd)) })] })] })), 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.form.limitLabel }), jsx(Input, { className: "focus-visible:border-blue-500 focus-visible:ring-1 focus-visible:ring-blue-500", value: limit, onChange: (e) => setLimit(e.target.value), placeholder: labels.form.limitPlaceholder, inputMode: "decimal", "data-testid": `${testId}-limit-input` }), limitTouched && !limitValid && (jsx("p", { className: "text-xs text-red-500", "data-testid": `${testId}-limit-error`, children: labels.form.limitError }))] }), jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { className: "text-xs font-medium text-gray-700", children: labels.form.intervalLabel }), jsxs(Select$1, { value: interval, onValueChange: (value) => setInterval(value), children: [jsx(SelectTrigger, { className: "h-9", "data-testid": `${testId}-interval-select`, children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "day", children: labels.form.intervalOptions.day }), jsx(SelectItem, { value: "week", children: labels.form.intervalOptions.week }), jsx(SelectItem, { value: "month", children: labels.form.intervalOptions.month }), jsx(SelectItem, { value: "year", children: labels.form.intervalOptions.year })] })] })] })] }), 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.form.enforcementLabel }), jsxs(Select$1, { value: enforcement, onValueChange: (value) => setEnforcement(value), children: [jsx(SelectTrigger, { className: "h-9", "data-testid": `${testId}-enforcement-select`, children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "block", children: labels.form.enforcementOptions.block }), jsx(SelectItem, { value: "alert_only", children: labels.form.enforcementOptions.alertOnly })] })] }), jsx("p", { className: "text-xs text-gray-500", children: enforcement === 'block'
|
|
189834
|
+
? labels.form.enforcementHelp.block
|
|
189835
|
+
: labels.form.enforcementHelp.alertOnly })] }), jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { className: "text-xs font-medium text-gray-700", children: labels.form.thresholdsLabel }), jsx(Input, { className: "focus-visible:border-blue-500 focus-visible:ring-1 focus-visible:ring-blue-500", value: thresholds, onChange: (e) => setThresholds(e.target.value), placeholder: labels.form.thresholdsPlaceholder, "data-testid": `${testId}-thresholds-input` }), thresholdsValid ? (jsx("p", { className: "text-xs text-gray-500", children: labels.form.thresholdsHelp })) : (jsx("p", { className: "text-xs text-red-500", "data-testid": `${testId}-thresholds-error`, children: labels.form.thresholdsError }))] })] })] }) }), jsxs("div", { className: "flex items-center justify-end gap-2", children: [onCancel && (jsx("button", { type: "button", onClick: onCancel, disabled: isSaving || isDeleting, "data-testid": `${testId}-cancel-button`, className: "inline-flex items-center rounded-md border border-gray-200 px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 disabled:cursor-not-allowed disabled:opacity-50", children: labels.form.cancel })), cap && onDelete && (jsx("button", { type: "button", onClick: onDelete, disabled: isSaving || isDeleting, "data-testid": `${testId}-delete-button`, className: "inline-flex items-center rounded-md border border-red-200 px-4 py-2 text-sm font-medium text-red-600 hover:bg-red-50 disabled:cursor-not-allowed disabled:opacity-50", children: isDeleting ? labels.form.removing : labels.form.remove })), jsx("button", { type: "button", onClick: handleSave, disabled: !canSave, "data-testid": `${testId}-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.form.saving : labels.form.save })] })] }));
|
|
189836
|
+
}
|
|
189837
|
+
|
|
189838
|
+
function errorStatus$2(error) {
|
|
189839
|
+
return error === null || error === void 0 ? void 0 : error.status;
|
|
189840
|
+
}
|
|
189841
|
+
function errorDetail$2(error) {
|
|
189842
|
+
const data = error === null || error === void 0 ? void 0 : error.data;
|
|
189843
|
+
if (typeof (data === null || data === void 0 ? void 0 : data.detail) === 'string')
|
|
189844
|
+
return data.detail;
|
|
189845
|
+
if (typeof (data === null || data === void 0 ? void 0 : data.error) === 'string')
|
|
189846
|
+
return data.error;
|
|
189847
|
+
return '';
|
|
189848
|
+
}
|
|
189849
|
+
function useSpendCaps({ tenantKey, mentorId, labels }) {
|
|
189850
|
+
const identityReady = Boolean(tenantKey && mentorId);
|
|
189851
|
+
// 404 is the documented "no cap configured yet" state for the singleton
|
|
189852
|
+
// GETs, not an error — the row simply may not exist (mirrors the grader
|
|
189853
|
+
// tab's config query, which 404s until first saved).
|
|
189854
|
+
const { data: agentCapData, error: agentCapError, isLoading: isAgentCapLoading, refetch: refetchAgentCap, } = useGetAgentSpendCapQuery({ org: tenantKey, mentorId }, { skip: !identityReady });
|
|
189855
|
+
const { data: tenantCapData, error: tenantCapError, isLoading: isTenantCapLoading, refetch: refetchTenantCap, } = useGetTenantSpendCapQuery({ org: tenantKey }, { skip: !tenantKey });
|
|
189856
|
+
// Derive the caps from the error state, not just from `data`: after a
|
|
189857
|
+
// delete, the explicit refetch 404s while RTK Query keeps the entry's
|
|
189858
|
+
// last fulfilled data — without this, the form would keep showing the
|
|
189859
|
+
// just-deleted cap. Keyed on error PRESENCE rather than `status === 404`
|
|
189860
|
+
// because `iblFetchBaseQuery` THROWS its errors and RTK serializes a
|
|
189861
|
+
// thrown plain object down to a bare SerializedError with no `status`.
|
|
189862
|
+
// Any singleton-GET error means there is no trustworthy cap: the
|
|
189863
|
+
// expected 404 is "no cap configured", and the 403 denied state renders
|
|
189864
|
+
// its own panel instead of the form.
|
|
189865
|
+
const agentCap = agentCapError ? undefined : agentCapData;
|
|
189866
|
+
const tenantCap = tenantCapError ? undefined : tenantCapData;
|
|
189867
|
+
const { data: userCaps = [], error: userCapsError, isLoading: isUserCapsLoading, refetch: refetchUserCaps, } = useListAgentUserSpendCapsQuery({ org: tenantKey, mentorId }, { skip: !identityReady });
|
|
189868
|
+
const [upsertAgentCap, { isLoading: isSavingAgentCap }] = useUpsertAgentSpendCapMutation();
|
|
189869
|
+
const [deleteAgentCap, { isLoading: isDeletingAgentCap }] = useDeleteAgentSpendCapMutation();
|
|
189870
|
+
const [upsertTenantCap, { isLoading: isSavingTenantCap }] = useUpsertTenantSpendCapMutation();
|
|
189871
|
+
const [deleteTenantCap, { isLoading: isDeletingTenantCap }] = useDeleteTenantSpendCapMutation();
|
|
189872
|
+
const [upsertUserCap, { isLoading: isSavingUserCap }] = useUpsertUserSpendCapMutation();
|
|
189873
|
+
const [deleteUserCap, { isLoading: isDeletingUserCap }] = useDeleteUserSpendCapMutation();
|
|
189874
|
+
const isAgentForbidden = errorStatus$2(agentCapError) === 403;
|
|
189875
|
+
const isTenantForbidden = errorStatus$2(tenantCapError) === 403;
|
|
189876
|
+
const isUsersForbidden = errorStatus$2(userCapsError) === 403;
|
|
189877
|
+
// Refetch the mutated scope explicitly after every successful save/delete
|
|
189878
|
+
// (same as the grader tab's `refetchConfig()`): the singleton GETs 404
|
|
189879
|
+
// until first created, and a cache entry whose only fetch errored isn't
|
|
189880
|
+
// reachable through the mutation's tag invalidation — the explicit
|
|
189881
|
+
// refetch keeps the form in sync in place regardless.
|
|
189882
|
+
const runSave = async (mutate, refetchScope) => {
|
|
189883
|
+
try {
|
|
189884
|
+
await mutate();
|
|
189885
|
+
refetchScope();
|
|
189886
|
+
toast.success(labels.toasts.saved);
|
|
189887
|
+
return true;
|
|
189888
|
+
}
|
|
189889
|
+
catch (error) {
|
|
189890
|
+
console.error('[AgentSpendCapsTab] save spend cap error', error);
|
|
189891
|
+
toast.error(errorDetail$2(error) || labels.toasts.saveError);
|
|
189892
|
+
return false;
|
|
189893
|
+
}
|
|
189894
|
+
};
|
|
189895
|
+
const runDelete = async (mutate, refetchScope) => {
|
|
189896
|
+
try {
|
|
189897
|
+
await mutate();
|
|
189898
|
+
refetchScope();
|
|
189899
|
+
toast.success(labels.toasts.deleted);
|
|
189900
|
+
return true;
|
|
189901
|
+
}
|
|
189902
|
+
catch (error) {
|
|
189903
|
+
console.error('[AgentSpendCapsTab] delete spend cap error', error);
|
|
189904
|
+
toast.error(errorDetail$2(error) || labels.toasts.deleteError);
|
|
189905
|
+
return false;
|
|
189906
|
+
}
|
|
189907
|
+
};
|
|
189908
|
+
const saveAgentCap = (formData) => runSave(() => upsertAgentCap({ org: tenantKey, mentorId, formData }).unwrap(), refetchAgentCap);
|
|
189909
|
+
const removeAgentCap = () => runDelete(() => deleteAgentCap({ org: tenantKey, mentorId }).unwrap(), refetchAgentCap);
|
|
189910
|
+
const saveTenantCap = (formData) => runSave(() => upsertTenantCap({ org: tenantKey, formData }).unwrap(), refetchTenantCap);
|
|
189911
|
+
const removeTenantCap = () => runDelete(() => deleteTenantCap({ org: tenantKey }).unwrap(), refetchTenantCap);
|
|
189912
|
+
const saveUserCap = (username, formData) => runSave(() => upsertUserCap({ org: tenantKey, mentorId, username, formData }).unwrap(), refetchUserCaps);
|
|
189913
|
+
const removeUserCap = (username) => runDelete(() => deleteUserCap({ org: tenantKey, mentorId, username }).unwrap(), refetchUserCaps);
|
|
189914
|
+
return {
|
|
189915
|
+
agentCap,
|
|
189916
|
+
isAgentCapLoading,
|
|
189917
|
+
isAgentForbidden,
|
|
189918
|
+
saveAgentCap,
|
|
189919
|
+
removeAgentCap,
|
|
189920
|
+
isSavingAgentCap,
|
|
189921
|
+
isDeletingAgentCap,
|
|
189922
|
+
tenantCap,
|
|
189923
|
+
isTenantCapLoading,
|
|
189924
|
+
isTenantForbidden,
|
|
189925
|
+
saveTenantCap,
|
|
189926
|
+
removeTenantCap,
|
|
189927
|
+
isSavingTenantCap,
|
|
189928
|
+
isDeletingTenantCap,
|
|
189929
|
+
userCaps,
|
|
189930
|
+
isUserCapsLoading,
|
|
189931
|
+
isUsersForbidden,
|
|
189932
|
+
saveUserCap,
|
|
189933
|
+
removeUserCap,
|
|
189934
|
+
isSavingUserCap,
|
|
189935
|
+
isDeletingUserCap,
|
|
189936
|
+
};
|
|
189937
|
+
}
|
|
189938
|
+
|
|
189939
|
+
/**
|
|
189940
|
+
* Tenant-settings Billing section for the workspace-wide LLM spend limit.
|
|
189941
|
+
* Renders the same `SpendCapForm` (usage strip included) as the agent
|
|
189942
|
+
* settings Billing tab, so both scopes present the identical editor.
|
|
189943
|
+
* Per-agent and per-user limits are managed in each agent's own Billing tab
|
|
189944
|
+
* (`AgentSpendCapsTab`), since user limits are keyed per agent.
|
|
189945
|
+
* Rendered inside `BillingTab`.
|
|
189946
|
+
*/
|
|
189947
|
+
function SpendLimitsSection({ tenantKey, labels: labelsOverride }) {
|
|
189948
|
+
const t = useT();
|
|
189949
|
+
const labels = React__default.useMemo(() => resolveSpendCapsTabLabels(labelsOverride, t), [labelsOverride, t]);
|
|
189950
|
+
const [isDeleteOpen, setIsDeleteOpen] = React__default.useState(false);
|
|
189951
|
+
const { tenantCap, isTenantCapLoading, isTenantForbidden, saveTenantCap, removeTenantCap, isSavingTenantCap, isDeletingTenantCap, } = useSpendCaps({ tenantKey, mentorId: '', labels });
|
|
189952
|
+
const handleConfirmDelete = async () => {
|
|
189953
|
+
const deleted = await removeTenantCap();
|
|
189954
|
+
if (deleted)
|
|
189955
|
+
setIsDeleteOpen(false);
|
|
189956
|
+
};
|
|
189957
|
+
// With no cap configured there are no reconciled counters, so surface the
|
|
189958
|
+
// workspace's actual spend from the financial analytics endpoint instead —
|
|
189959
|
+
// an admin should see what is being spent before deciding on a limit.
|
|
189960
|
+
const skipActualSpend = !tenantKey || isTenantCapLoading || isTenantForbidden || Boolean(tenantCap);
|
|
189961
|
+
const { data: monthlySpend } = useGetFinancialStatsQuery({ metric: 'monthly_costs', date_filter: 'all_time', platform_key: tenantKey }, { skip: skipActualSpend });
|
|
189962
|
+
const { data: totalSpend } = useGetFinancialStatsQuery({ metric: 'total_costs', all_time: 'true', date_filter: 'all_time', platform_key: tenantKey }, { skip: skipActualSpend });
|
|
189963
|
+
return (jsxs(Fragment$1, { children: [jsxs("div", { "data-testid": "spend-limits-workspace-section", className: "rounded-lg border border-[#e5e5e5] bg-white shadow-[0_1px_2px_0_rgba(0,0,0,0.05)] dark:bg-zinc-900 dark:border-zinc-800", children: [jsx("div", { className: "flex flex-col gap-2 p-4 sm:flex-row sm:items-start sm:justify-between", children: jsxs("div", { className: "space-y-0.5", children: [jsx("h3", { className: "text-[16px] font-medium leading-6 text-[#101828] dark:text-gray-100", children: labels.tenantSections.workspace.title }), jsx("p", { className: "text-[13px] font-normal leading-5 text-[#4a5565] dark:text-gray-400", children: labels.tenantSections.workspace.description })] }) }), jsx("div", { className: "border-t border-[#e5e7eb] dark:border-zinc-800 px-4 py-3", children: isTenantCapLoading ? (jsx("div", { className: "flex items-center justify-center py-8", "data-testid": "tenant-cap-loading", children: jsx(Spinner, {}) })) : isTenantForbidden ? (jsxs("div", { className: "flex flex-col items-center gap-2 rounded-lg border border-gray-200 bg-gray-50 px-4 py-10 text-center", "data-testid": "spend-limits-denied", children: [jsx(ShieldAlert, { className: "h-6 w-6 text-gray-400" }), jsx("p", { className: "text-sm font-medium text-gray-900", children: labels.denied.title }), jsx("p", { className: "max-w-sm text-xs text-gray-600", children: labels.denied.description })] })) : (jsxs("div", { className: "space-y-4", children: [!tenantCap && (monthlySpend != null || totalSpend != null) && (jsxs("div", { className: "space-y-1", "data-testid": "workspace-actual-spend-stats", children: [jsxs("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2", children: [monthlySpend != null && (jsxs("div", { className: "space-y-0.5", children: [jsx("p", { className: "text-[11px] font-medium uppercase tracking-wide text-[#6a7282] dark:text-gray-400", children: labels.tenantSections.noCapStats.spentThisMonth }), jsx("p", { className: "text-[18px] font-semibold leading-6 text-[#101828] dark:text-gray-100", "data-testid": "workspace-actual-spend-month", children: formatUsd$1(String(monthlySpend.value)) })] })), totalSpend != null && (jsxs("div", { className: "space-y-0.5", children: [jsx("p", { className: "text-[11px] font-medium uppercase tracking-wide text-[#6a7282] dark:text-gray-400", children: labels.tenantSections.noCapStats.spentAllTime }), jsx("p", { className: "text-[18px] font-semibold leading-6 text-[#101828] dark:text-gray-100", "data-testid": "workspace-actual-spend-total", children: formatUsd$1(String(totalSpend.value)) })] }))] }), jsx("p", { className: "text-[12px] font-normal leading-4 text-[#6a7282] dark:text-gray-500", children: labels.tenantSections.noCapStats.hint })] })), jsx(SpendCapForm, { labels: labels, cap: tenantCap, isSaving: isSavingTenantCap, isDeleting: isDeletingTenantCap, onSave: saveTenantCap, onDelete: () => setIsDeleteOpen(true), testId: "tenant-cap" })] })) })] }), jsx(Dialog, { open: isDeleteOpen, onOpenChange: () => setIsDeleteOpen(false), children: jsxs(DialogContent, { "aria-describedby": "tenant-cap-delete-description", "data-testid": "tenant-cap-delete-modal", children: [jsx(DialogHeader, { children: jsx(DialogTitle, { className: "ibl-dialog-title", children: labels.deleteModal.title }) }), jsx("div", { className: "my-5", id: "tenant-cap-delete-description", children: jsx("p", { className: "text-sm text-[#646464]", children: labels.deleteModal.description }) }), jsxs(DialogFooter, { className: "gap-3", children: [jsx(Button$1, { variant: "outline", onClick: () => setIsDeleteOpen(false), children: labels.deleteModal.cancel }), jsx(Button$1, { className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", onClick: handleConfirmDelete, disabled: isDeletingTenantCap, "data-testid": "tenant-cap-delete-confirm", children: isDeletingTenantCap ? labels.form.removing : labels.deleteModal.confirm })] })] }) })] }));
|
|
189964
|
+
}
|
|
189965
|
+
|
|
189966
|
+
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$9(r,n){return r===n}function a$3(n,t,a){var o=i$9,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]}
|
|
189967
|
+
|
|
189968
|
+
const Table$1 = React.forwardRef(({ className, ...props }, ref) => (jsx("div", { className: "relative w-full overflow-auto", children: jsx("table", { ref: ref, className: cn("w-full caption-bottom text-sm", className), ...props }) })));
|
|
189969
|
+
Table$1.displayName = "Table";
|
|
189970
|
+
const TableHeader$1 = React.forwardRef(({ className, ...props }, ref) => (jsx("thead", { ref: ref, className: cn("[&_tr]:border-b", className), ...props })));
|
|
189971
|
+
TableHeader$1.displayName = "TableHeader";
|
|
189972
|
+
const TableBody = React.forwardRef(({ className, ...props }, ref) => (jsx("tbody", { ref: ref, className: cn("[&_tr:last-child]:border-0", className), ...props })));
|
|
189973
|
+
TableBody.displayName = "TableBody";
|
|
189974
|
+
const TableFooter = React.forwardRef(({ className, ...props }, ref) => (jsx("tfoot", { ref: ref, className: cn("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", className), ...props })));
|
|
189975
|
+
TableFooter.displayName = "TableFooter";
|
|
189976
|
+
const TableRow$1 = React.forwardRef(({ className, ...props }, ref) => (jsx("tr", { ref: ref, className: cn("border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted", className), ...props })));
|
|
189977
|
+
TableRow$1.displayName = "TableRow";
|
|
189978
|
+
const TableHead = React.forwardRef(({ className, ...props }, ref) => (jsx("th", { ref: ref, className: cn("h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", className), ...props })));
|
|
189979
|
+
TableHead.displayName = "TableHead";
|
|
189980
|
+
const TableCell$1 = React.forwardRef(({ className, ...props }, ref) => (jsx("td", { ref: ref, className: cn("p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", className), ...props })));
|
|
189981
|
+
TableCell$1.displayName = "TableCell";
|
|
189982
|
+
const TableCaption = React.forwardRef(({ className, ...props }, ref) => (jsx("caption", { ref: ref, className: cn("mt-4 text-sm text-muted-foreground", className), ...props })));
|
|
189983
|
+
TableCaption.displayName = "TableCaption";
|
|
189984
|
+
|
|
189985
|
+
/**
|
|
189986
|
+
* Single-select searchable picker: a deb-ounce-agnostic input + absolute
|
|
189987
|
+
* results dropdown (the add-access pattern), collapsing to a removable chip
|
|
189988
|
+
* once something is picked. The caller owns the search term and the fetched
|
|
189989
|
+
* options so any query hook can back it.
|
|
189990
|
+
*/
|
|
189991
|
+
function SearchSelect({ labels, selected, onSelect, onClear, searchTerm, onSearchTermChange, options, isLoading, minChars = 2, testId, }) {
|
|
189992
|
+
const [isOpen, setIsOpen] = React__default.useState(false);
|
|
189993
|
+
const blurTimeout = React__default.useRef(null);
|
|
189994
|
+
React__default.useEffect(() => () => {
|
|
189995
|
+
if (blurTimeout.current)
|
|
189996
|
+
clearTimeout(blurTimeout.current);
|
|
189997
|
+
}, []);
|
|
189998
|
+
if (selected) {
|
|
189999
|
+
return (jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { className: "text-xs font-medium text-gray-700", children: labels.label }), jsxs("div", { className: "flex items-center justify-between gap-2 rounded-md border border-gray-200 bg-gray-50 px-3 py-2", "data-testid": `${testId}-selected`, children: [jsxs("div", { className: "min-w-0", children: [jsx("p", { className: "truncate text-sm font-medium text-gray-900", children: selected.label }), selected.sublabel && (jsx("p", { className: "truncate text-xs text-gray-600", children: selected.sublabel }))] }), jsxs(Button$1, { type: "button", size: "icon", variant: "ghost", className: "h-6 w-6 flex-shrink-0 text-gray-500 hover:text-red-600", onClick: onClear, "data-testid": `${testId}-clear`, children: [jsx(X$2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }), jsx("span", { className: "sr-only", children: labels.clear })] })] })] }));
|
|
190000
|
+
}
|
|
190001
|
+
return (jsxs("div", { className: "space-y-1.5", children: [jsx(Label$1, { className: "text-xs font-medium text-gray-700", htmlFor: `${testId}-input`, children: labels.label }), jsxs("div", { className: "relative", children: [jsx(Input, { id: `${testId}-input`, className: "focus-visible:border-blue-500 focus-visible:ring-1 focus-visible:ring-blue-500", value: searchTerm, onChange: (e) => onSearchTermChange(e.target.value), onFocus: () => {
|
|
190002
|
+
if (blurTimeout.current)
|
|
190003
|
+
clearTimeout(blurTimeout.current);
|
|
190004
|
+
setIsOpen(true);
|
|
190005
|
+
}, onBlur: () => {
|
|
190006
|
+
blurTimeout.current = setTimeout(() => setIsOpen(false), 100);
|
|
190007
|
+
}, placeholder: labels.placeholder, autoComplete: "off", "aria-autocomplete": "list", "aria-expanded": isOpen, "data-testid": `${testId}-input` }), isOpen && (jsx("div", { className: "absolute top-full right-0 left-0 z-50 mt-1 max-h-64 overflow-y-auto rounded-md border border-gray-200 bg-white shadow-lg", "data-testid": `${testId}-results`, children: searchTerm.trim().length < minChars ? (jsx("div", { className: "px-3 py-2 text-sm text-gray-600", children: labels.typeToSearch })) : isLoading ? (jsxs("div", { className: "flex items-center gap-2 px-3 py-2 text-sm text-gray-600", children: [jsx(LoaderCircle, { className: "h-3.5 w-3.5 animate-spin", "aria-hidden": "true" }), labels.searching] })) : options.length > 0 ? (options.map((option) => (jsxs("button", { type: "button", className: "flex w-full flex-col items-start gap-0.5 px-3 py-2 text-left hover:bg-gray-50", onMouseDown: (event) => event.preventDefault(), onClick: () => {
|
|
190008
|
+
onSelect(option);
|
|
190009
|
+
setIsOpen(false);
|
|
190010
|
+
}, "data-testid": `${testId}-option-${option.value}`, children: [jsx("span", { className: "text-sm font-medium text-gray-900", children: option.label }), option.sublabel && (jsx("span", { className: "text-xs text-gray-600", children: option.sublabel }))] }, option.value)))) : (jsx("div", { className: "px-3 py-2 text-sm text-gray-600", "data-testid": `${testId}-no-results`, children: labels.noResults })) }))] })] }));
|
|
190011
|
+
}
|
|
190012
|
+
|
|
190013
|
+
/**
|
|
190014
|
+
* The shared `Profile` viewer in a dialog — the same modal the tenant
|
|
190015
|
+
* Management tab opens when a user row is clicked. Controlled variant for
|
|
190016
|
+
* hosts that trigger it from a whole table row.
|
|
190017
|
+
*/
|
|
190018
|
+
function UserProfileDialog({ tenantKey, username, isOpen, onClose }) {
|
|
190019
|
+
const t = useT();
|
|
190020
|
+
if (!isOpen)
|
|
190021
|
+
return null;
|
|
190022
|
+
return (jsx(Dialog, { open: true, onOpenChange: onClose, children: jsxs(DialogContent, { autoFocus: false, className: "profile-component-dialog max-w-7xl w-[85vw] gap-0 overflow-hidden bg-white p-0", style: { height: '85vh', display: 'flex', flexDirection: 'column' }, "data-testid": "user-profile-link-dialog", children: [jsx(DialogTitle, { className: "sr-only", children: t('tabsUserstab.profileDialogTitle') }), jsx(DialogDescription, { className: "sr-only", children: t('tabsUserstab.profileDialogDescription') }), jsx(Profile, { tenant: tenantKey, tenants: [], username: username, onClose: onClose })] }) }));
|
|
190023
|
+
}
|
|
190024
|
+
/**
|
|
190025
|
+
* Clickable user identity that opens the shared `Profile` viewer in a dialog
|
|
190026
|
+
* — the same modal the tenant Management tab opens when a user row is
|
|
190027
|
+
* clicked. Used by the per-user limits table and the user-limit modal.
|
|
190028
|
+
*/
|
|
190029
|
+
function UserProfileLink({ tenantKey, username, display, className, testId, }) {
|
|
190030
|
+
const t = useT();
|
|
190031
|
+
const [isOpen, setIsOpen] = React__default.useState(false);
|
|
190032
|
+
return (jsxs(Fragment$1, { children: [jsx("button", { type: "button", onClick: (event) => {
|
|
190033
|
+
event.stopPropagation();
|
|
190034
|
+
setIsOpen(true);
|
|
190035
|
+
}, className: cn('max-w-full cursor-pointer truncate text-left hover:text-blue-600 hover:underline focus:text-blue-600 focus:outline-none', className), "aria-label": t('tabsUserstab.viewProfileAriaLabel', { name: display }), "data-testid": testId, children: display }), jsx(UserProfileDialog, { tenantKey: tenantKey, username: username, isOpen: isOpen, onClose: () => setIsOpen(false) })] }));
|
|
190036
|
+
}
|
|
190037
|
+
|
|
190038
|
+
/**
|
|
190039
|
+
* Add/edit dialog for one per-user spend limit, mirroring the grader
|
|
190040
|
+
* criterion modal: create mode starts with a searchable platform-user picker
|
|
190041
|
+
* (no free-text usernames), edit mode goes straight to the form for the
|
|
190042
|
+
* cap's user. The form's Cancel/Save footer acts as the dialog footer.
|
|
190043
|
+
*/
|
|
190044
|
+
function UserCapModal({ isOpen, onClose, cap, labels, tenantKey, isSaving, onSave }) {
|
|
190045
|
+
var _a, _b, _c, _d, _e;
|
|
190046
|
+
const isEditing = Boolean(cap);
|
|
190047
|
+
const [pickedUser, setPickedUser] = React__default.useState(null);
|
|
190048
|
+
const [userSearch, setUserSearch] = React__default.useState('');
|
|
190049
|
+
// Reset the picker each time the modal opens for a create.
|
|
190050
|
+
React__default.useEffect(() => {
|
|
190051
|
+
if (!isOpen)
|
|
190052
|
+
return;
|
|
190053
|
+
setPickedUser(null);
|
|
190054
|
+
setUserSearch('');
|
|
190055
|
+
}, [isOpen, cap]);
|
|
190056
|
+
const [debouncedUserSearch] = a$3(userSearch, 300);
|
|
190057
|
+
const shouldSearchUsers = Boolean(isOpen && !isEditing && tenantKey && debouncedUserSearch.trim().length >= 2);
|
|
190058
|
+
const { data: usersData, isFetching: isSearchingUsers } = usePlatformUsersQuery({
|
|
190059
|
+
page: 1,
|
|
190060
|
+
page_size: 20,
|
|
190061
|
+
platform_key: tenantKey,
|
|
190062
|
+
platform_org: tenantKey,
|
|
190063
|
+
query: debouncedUserSearch.trim(),
|
|
190064
|
+
return_policies: 'false',
|
|
190065
|
+
}, { skip: !shouldSearchUsers });
|
|
190066
|
+
// The platform users endpoint returns either `results` or `results.data`
|
|
190067
|
+
// depending on whether policies were requested.
|
|
190068
|
+
const rawResults = usersData === null || usersData === void 0 ? void 0 : usersData.results;
|
|
190069
|
+
const foundUsers = Array.isArray(rawResults)
|
|
190070
|
+
? rawResults
|
|
190071
|
+
: ((_a = rawResults === null || rawResults === void 0 ? void 0 : rawResults.data) !== null && _a !== void 0 ? _a : []);
|
|
190072
|
+
// Deliberately email-only in the UI (no name/username noise); the cap API
|
|
190073
|
+
// is still keyed by username, which rides along as the option value.
|
|
190074
|
+
const userOptions = foundUsers
|
|
190075
|
+
.filter((user) => Boolean(user.username) && Boolean(user.email))
|
|
190076
|
+
.map((user) => ({
|
|
190077
|
+
value: user.username,
|
|
190078
|
+
label: user.email,
|
|
190079
|
+
}));
|
|
190080
|
+
const username = isEditing ? ((_b = cap === null || cap === void 0 ? void 0 : cap.username) !== null && _b !== void 0 ? _b : '') : ((_c = pickedUser === null || pickedUser === void 0 ? void 0 : pickedUser.value) !== null && _c !== void 0 ? _c : '');
|
|
190081
|
+
const handleSave = async (formData) => {
|
|
190082
|
+
if (!username)
|
|
190083
|
+
return;
|
|
190084
|
+
const saved = await onSave(username, formData);
|
|
190085
|
+
if (saved)
|
|
190086
|
+
onClose();
|
|
190087
|
+
};
|
|
190088
|
+
return (jsx(Dialog, { open: isOpen, onOpenChange: onClose, children: jsxs(DialogContent, { "aria-describedby": "user-cap-modal-description", "data-testid": "user-cap-modal", className: "flex max-h-[85vh] flex-col sm:max-w-xl", children: [jsx(DialogHeader, { className: "flex-shrink-0", children: jsx(DialogTitle, { className: "ibl-dialog-title", children: isEditing ? labels.users.editTitle : labels.users.addTitle }) }), jsxs("div", { className: cn('min-h-0 flex-1 space-y-4 overflow-y-auto',
|
|
190089
|
+
// Reserve room for the search results dropdown while no user is
|
|
190090
|
+
// picked yet — otherwise the scroll container clips it.
|
|
190091
|
+
!isEditing && !pickedUser && 'min-h-[340px]'), id: "user-cap-modal-description", children: [isEditing ? (jsx("p", { className: "text-sm text-[#646464]", "data-testid": "user-cap-modal-username", children: jsx(UserProfileLink, { tenantKey: tenantKey, username: (_d = cap === null || cap === void 0 ? void 0 : cap.username) !== null && _d !== void 0 ? _d : '', display: (cap === null || cap === void 0 ? void 0 : cap.email) || ((_e = cap === null || cap === void 0 ? void 0 : cap.username) !== null && _e !== void 0 ? _e : ''), className: "inline", testId: "user-cap-modal-profile-link" }) })) : (jsx(SearchSelect, { labels: labels.users.picker, selected: pickedUser, onSelect: setPickedUser, onClear: () => setPickedUser(null), searchTerm: userSearch, onSearchTermChange: setUserSearch, options: userOptions, isLoading: isSearchingUsers, testId: "user-cap-modal-user-picker" })), Boolean(username) && (jsx(SpendCapForm, { labels: labels, cap: cap !== null && cap !== void 0 ? cap : undefined, isSaving: isSaving, isDeleting: false, onSave: handleSave, onCancel: onClose, testId: "user-cap-modal" }))] })] }) }));
|
|
190092
|
+
}
|
|
190093
|
+
|
|
190094
|
+
/**
|
|
190095
|
+
* Per-user limits for one agent: a list of the explicit (user, agent) caps.
|
|
190096
|
+
* Add and edit both go through the `UserCapModal` popup (add picks the user
|
|
190097
|
+
* via a platform-user search — no free-text usernames); delete confirms in a
|
|
190098
|
+
* dialog.
|
|
190099
|
+
*/
|
|
190100
|
+
function UserCapsSection({ labels, tenantKey, caps, isLoading, isSaving, isDeleting, onSave, onDelete, }) {
|
|
190101
|
+
var _a;
|
|
190102
|
+
const [isModalOpen, setIsModalOpen] = React__default.useState(false);
|
|
190103
|
+
const [editingCap, setEditingCap] = React__default.useState(null);
|
|
190104
|
+
// The cap queued for deletion — kept whole so the dialog can show the
|
|
190105
|
+
// user's email while the delete call is still keyed by username.
|
|
190106
|
+
const [pendingDelete, setPendingDelete] = React__default.useState(null);
|
|
190107
|
+
// Whole-row click opens the user's profile (Management-tab pattern).
|
|
190108
|
+
const [profileCap, setProfileCap] = React__default.useState(null);
|
|
190109
|
+
const openAdd = () => {
|
|
190110
|
+
setEditingCap(null);
|
|
190111
|
+
setIsModalOpen(true);
|
|
190112
|
+
};
|
|
190113
|
+
const openEdit = (cap) => {
|
|
190114
|
+
setEditingCap(cap);
|
|
190115
|
+
setIsModalOpen(true);
|
|
190116
|
+
};
|
|
190117
|
+
// Status toggle saves immediately (datasets-table pattern) — the PUT is an
|
|
190118
|
+
// upsert, so the cap's current fields ride along with the flipped flag.
|
|
190119
|
+
const toggleEnabled = (cap, checked) => {
|
|
190120
|
+
var _a;
|
|
190121
|
+
void onSave((_a = cap.username) !== null && _a !== void 0 ? _a : '', {
|
|
190122
|
+
interval_type: cap.interval_type,
|
|
190123
|
+
max_cost_usd: cap.max_cost_usd,
|
|
190124
|
+
enforcement: cap.enforcement,
|
|
190125
|
+
alert_thresholds: cap.alert_thresholds,
|
|
190126
|
+
enabled: checked,
|
|
190127
|
+
});
|
|
190128
|
+
};
|
|
190129
|
+
const handleConfirmDelete = async () => {
|
|
190130
|
+
var _a;
|
|
190131
|
+
if (!pendingDelete)
|
|
190132
|
+
return;
|
|
190133
|
+
const deleted = await onDelete((_a = pendingDelete.username) !== null && _a !== void 0 ? _a : '');
|
|
190134
|
+
if (deleted)
|
|
190135
|
+
setPendingDelete(null);
|
|
190136
|
+
};
|
|
190137
|
+
return (jsxs("div", { className: "space-y-3", "data-testid": "user-caps-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.users.sectionTitle }), jsx("p", { className: "text-xs text-gray-600", children: labels.users.sectionDescription })] }), jsxs(Button$1, { className: "ibl-button-primary flex-shrink-0", onClick: openAdd, disabled: isSaving || isDeleting, "data-testid": "user-caps-add-button", children: [jsx(Plus, { className: "mr-1 h-4 w-4" }), labels.users.addButton] })] }), jsx("div", { className: "overflow-hidden rounded-md border", "data-testid": "user-caps-list", 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", "data-testid": "user-caps-loading", 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.users.table.userColumn }), jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: labels.users.table.limitColumn }), jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: labels.users.table.statusColumn }), jsx(TableHead, { className: "w-10 p-3" })] }) }), jsx(TableBody, { children: caps.length === 0 ? (jsx(TableRow$1, { children: jsx(TableCell$1, { colSpan: 4, className: "p-4 text-center text-[#646464]", "data-testid": "user-caps-empty", children: labels.users.emptyState }) })) : (caps.map((cap) => {
|
|
190138
|
+
var _a;
|
|
190139
|
+
const username = (_a = cap.username) !== null && _a !== void 0 ? _a : '';
|
|
190140
|
+
// Email-first everywhere the user is shown or announced;
|
|
190141
|
+
// testids and mutation keys stay on the backend's
|
|
190142
|
+
// username key.
|
|
190143
|
+
const displayName = cap.email || username;
|
|
190144
|
+
return (jsxs(TableRow$1, { onClick: () => setProfileCap(cap), className: "cursor-pointer border-b transition-colors last:border-0 hover:bg-gray-50", "data-testid": `user-cap-row-${username}`, children: [jsx(TableCell$1, { className: "max-w-[240px] p-3 font-medium whitespace-nowrap text-[#646464]", children: jsx(UserProfileLink, { tenantKey: tenantKey, username: username, display: displayName, testId: `user-cap-profile-link-${username}` }) }), jsxs(TableCell$1, { className: "p-3 whitespace-nowrap text-[#646464]", children: [formatUsd$1(cap.max_cost_usd), " /", ' ', labels.form.intervalOptions[cap.interval_type]] }), jsx(TableCell$1, { className: "p-3", onClick: (e) => e.stopPropagation(), children: jsxs("div", { className: "flex items-center gap-1.5", children: [jsx(CustomSwitch, { checked: cap.enabled, onCheckedChange: (checked) => toggleEnabled(cap, checked), disabled: isSaving || isDeleting, "aria-label": labels.users.statusToggleAria(displayName), "data-testid": `user-cap-status-switch-${username}` }), cap.is_exceeded && (jsx("span", { className: "rounded-full bg-red-100 px-2 py-0.5 text-[11px] font-medium text-red-700", children: labels.usage.exceededBadge })), cap.enforcement === 'alert_only' && (jsx("span", { className: "rounded-full bg-amber-100 px-2 py-0.5 text-[11px] font-medium text-amber-800", children: labels.usage.alertOnlyBadge }))] }) }), jsx(TableCell$1, { className: "p-3 text-right", onClick: (e) => e.stopPropagation(), children: jsxs(DropdownMenu, { children: [jsx(DropdownMenuTrigger, { asChild: true, children: jsx(Button$1, { type: "button", variant: "ghost", size: "icon", "aria-label": labels.users.actionsAria(displayName), "data-testid": `user-cap-actions-${username}`, children: jsx(Ellipsis, { className: "h-4 w-4" }) }) }), jsxs(DropdownMenuContent, { align: "end", children: [jsxs(DropdownMenuItem, { onClick: () => openEdit(cap), "data-testid": `user-cap-edit-${username}`, children: [jsx(Pencil, { className: "mr-2 h-4 w-4" }), labels.users.edit] }), jsxs(DropdownMenuItem, { className: "text-red-600 focus:text-red-700", onClick: () => setPendingDelete(cap), "data-testid": `user-cap-delete-${username}`, children: [jsx(Trash2, { className: "mr-2 h-4 w-4" }), labels.users.delete] })] })] }) })] }, cap.id));
|
|
190145
|
+
})) })] })) }) }) }), jsx(UserProfileDialog, { tenantKey: tenantKey, username: (_a = profileCap === null || profileCap === void 0 ? void 0 : profileCap.username) !== null && _a !== void 0 ? _a : '', isOpen: profileCap !== null, onClose: () => setProfileCap(null) }), jsx(UserCapModal, { isOpen: isModalOpen, onClose: () => setIsModalOpen(false), cap: editingCap, labels: labels, tenantKey: tenantKey, isSaving: isSaving, onSave: onSave }), jsx(Dialog, { open: pendingDelete !== null, onOpenChange: () => setPendingDelete(null), children: jsxs(DialogContent, { "aria-describedby": "user-cap-delete-description", "data-testid": "user-cap-delete-modal", children: [jsx(DialogHeader, { children: jsx(DialogTitle, { className: "ibl-dialog-title", children: labels.deleteModal.title }) }), jsx("div", { className: "my-5", id: "user-cap-delete-description", children: jsxs("p", { className: "text-sm text-[#646464]", children: [jsx("span", { className: "font-bold italic", children: pendingDelete ? pendingDelete.email || pendingDelete.username : '' }), ' ', "\u2014 ", labels.deleteModal.description] }) }), jsxs(DialogFooter, { className: "gap-3", children: [jsx(Button$1, { variant: "outline", onClick: () => setPendingDelete(null), children: labels.deleteModal.cancel }), jsx(Button$1, { className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", onClick: handleConfirmDelete, disabled: isDeleting, "data-testid": "user-cap-delete-confirm", children: isDeleting ? labels.form.removing : labels.deleteModal.confirm })] })] }) })] }));
|
|
190146
|
+
}
|
|
190147
|
+
|
|
190148
|
+
const AgentSettingsContext = React__default.createContext(null);
|
|
190149
|
+
function AgentSettingsProvider({ children, ...value }) {
|
|
190150
|
+
const ctx = React__default.useMemo(() => value,
|
|
190151
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
190152
|
+
[
|
|
190153
|
+
value.tenantKey,
|
|
190154
|
+
value.mentorId,
|
|
190155
|
+
value.username,
|
|
190156
|
+
value.enableRBAC,
|
|
190157
|
+
value.rbacPermissions,
|
|
190158
|
+
value.executeGatedAction,
|
|
190159
|
+
value.visibilityOptions,
|
|
190160
|
+
]);
|
|
190161
|
+
return jsx(AgentSettingsContext.Provider, { value: ctx, children: children });
|
|
190162
|
+
}
|
|
190163
|
+
/**
|
|
190164
|
+
* Read the shared agent identity / config from the nearest
|
|
190165
|
+
* `<AgentSettingsProvider>` if one is present, returning `null` otherwise.
|
|
190166
|
+
*
|
|
190167
|
+
* Use this when a tab can also be driven by direct props (i.e. the
|
|
190168
|
+
* `<AgentSettingsProvider>` wrapper is optional). For tabs that strictly
|
|
190169
|
+
* require the provider, use `useAgentSettings`.
|
|
190170
|
+
*/
|
|
190171
|
+
function useAgentSettingsOptional() {
|
|
190172
|
+
return React__default.useContext(AgentSettingsContext);
|
|
190173
|
+
}
|
|
190174
|
+
/**
|
|
190175
|
+
* Read the shared agent identity / config from the nearest
|
|
190176
|
+
* `<AgentSettingsProvider>`. Throws if called outside a provider — this is
|
|
190177
|
+
* intentional: every tab MUST be wrapped.
|
|
190178
|
+
*/
|
|
190179
|
+
function useAgentSettings() {
|
|
190180
|
+
const ctx = React__default.useContext(AgentSettingsContext);
|
|
190181
|
+
if (!ctx) {
|
|
190182
|
+
throw new Error('useAgentSettings must be used within an <AgentSettingsProvider>. ' +
|
|
190183
|
+
'Wrap your tab components with <AgentSettingsProvider tenantKey=... mentorId=... username=... enableRBAC=...>.');
|
|
190184
|
+
}
|
|
190185
|
+
return ctx;
|
|
190186
|
+
}
|
|
190187
|
+
|
|
190188
|
+
const SPEND_CAPS_SUB_TABS = {
|
|
190189
|
+
agent: 'agent',
|
|
190190
|
+
users: 'users',
|
|
190191
|
+
};
|
|
190192
|
+
function DeniedState({ labels }) {
|
|
190193
|
+
return (jsxs("div", { className: "flex flex-col items-center gap-2 rounded-lg border border-gray-200 bg-gray-50 px-4 py-10 text-center", "data-testid": "spend-caps-denied", children: [jsx(ShieldAlert, { className: "h-6 w-6 text-gray-400" }), jsx("p", { className: "text-sm font-medium text-gray-900", children: labels.title }), jsx("p", { className: "max-w-sm text-xs text-gray-600", children: labels.description })] }));
|
|
190194
|
+
}
|
|
190195
|
+
/**
|
|
190196
|
+
* "Billing" tab of the agent settings modal, with two sub-tabs matching the
|
|
190197
|
+
* agent-level backend scopes: the agent's own spend limit and per-user limits
|
|
190198
|
+
* on this agent (user picked through a platform-user search). The
|
|
190199
|
+
* workspace-wide limit is managed in the tenant settings Billing tab instead
|
|
190200
|
+
* (`SpendLimitsSection` inside `BillingTab`). Reads/writes the RBAC-gated
|
|
190201
|
+
* admin endpoints; a 403 renders a friendly denied state per scope.
|
|
190202
|
+
*/
|
|
190203
|
+
function AgentSpendCapsTab({ labels: labelsOverride, tenantKey: tenantKeyProp, mentorId: mentorIdProp, headerClassName, bodyClassName, }) {
|
|
190204
|
+
var _a, _b;
|
|
190205
|
+
const ctx = useAgentSettingsOptional();
|
|
190206
|
+
const tenantKey = (_a = tenantKeyProp !== null && tenantKeyProp !== void 0 ? tenantKeyProp : ctx === null || ctx === void 0 ? void 0 : ctx.tenantKey) !== null && _a !== void 0 ? _a : '';
|
|
190207
|
+
const mentorId = (_b = mentorIdProp !== null && mentorIdProp !== void 0 ? mentorIdProp : ctx === null || ctx === void 0 ? void 0 : ctx.mentorId) !== null && _b !== void 0 ? _b : '';
|
|
190208
|
+
const t = useT();
|
|
190209
|
+
const labels = React__default.useMemo(() => resolveSpendCapsTabLabels(labelsOverride, t), [labelsOverride, t]);
|
|
190210
|
+
const { agentCap, isAgentCapLoading, isAgentForbidden, saveAgentCap, removeAgentCap, isSavingAgentCap, isDeletingAgentCap, userCaps, isUserCapsLoading, isUsersForbidden, saveUserCap, removeUserCap, isSavingUserCap, isDeletingUserCap, } = useSpendCaps({ tenantKey, mentorId, labels });
|
|
190211
|
+
const [isDeleteOpen, setIsDeleteOpen] = React__default.useState(false);
|
|
190212
|
+
// Height lock: the two sub-tabs have different natural heights, which
|
|
190213
|
+
// would make a content-sized host (the tenant Agent Limits popup) jump on
|
|
190214
|
+
// every switch. Ratchet the tallest rendered height into a min-height so
|
|
190215
|
+
// switching never shrinks the tab — without padding it beyond its own
|
|
190216
|
+
// natural size.
|
|
190217
|
+
const [subTab, setSubTab] = React__default.useState(SPEND_CAPS_SUB_TABS.agent);
|
|
190218
|
+
const tabsRef = React__default.useRef(null);
|
|
190219
|
+
const [minTabsHeight, setMinTabsHeight] = React__default.useState(0);
|
|
190220
|
+
React__default.useLayoutEffect(() => {
|
|
190221
|
+
var _a, _b;
|
|
190222
|
+
const height = (_b = (_a = tabsRef.current) === null || _a === void 0 ? void 0 : _a.offsetHeight) !== null && _b !== void 0 ? _b : 0;
|
|
190223
|
+
setMinTabsHeight((current) => Math.max(current, height));
|
|
190224
|
+
}, [subTab]);
|
|
190225
|
+
const handleConfirmDelete = async () => {
|
|
190226
|
+
const deleted = await removeAgentCap();
|
|
190227
|
+
if (deleted)
|
|
190228
|
+
setIsDeleteOpen(false);
|
|
190229
|
+
};
|
|
190230
|
+
return (jsxs(Fragment$1, { children: [jsx("div", { className: cn('flex h-[73px] flex-shrink-0 items-center border-b border-gray-200 bg-white p-4 lg:block', headerClassName), 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: cn('flex-1 space-y-4 p-3 lg:p-4', bodyClassName), style: { overflowY: 'auto', overflowX: 'hidden' }, "data-testid": "spend-caps-tab-body", children: jsxs(Tabs, { value: subTab, onValueChange: setSubTab, className: "w-full", ref: tabsRef, style: minTabsHeight ? { minHeight: minTabsHeight } : undefined, children: [jsxs(TabsList, { "aria-label": labels.header.title, "data-testid": "spend-caps-sub-tabs", children: [jsx(TabsTrigger, { value: SPEND_CAPS_SUB_TABS.agent, "data-testid": "spend-caps-sub-tab-agent", children: labels.subTabs.agent }), jsx(TabsTrigger, { value: SPEND_CAPS_SUB_TABS.users, "data-testid": "spend-caps-sub-tab-users", children: labels.subTabs.users })] }), jsx(TabsContent, { value: SPEND_CAPS_SUB_TABS.agent, className: "space-y-4", children: isAgentCapLoading ? (jsx("div", { className: "flex items-center justify-center py-12", "data-testid": "agent-cap-loading", children: jsx(Spinner, {}) })) : isAgentForbidden ? (jsx(DeniedState, { labels: labels.denied })) : (jsx(SpendCapForm, { labels: labels, cap: agentCap, isSaving: isSavingAgentCap, isDeleting: isDeletingAgentCap, onSave: saveAgentCap, onDelete: () => setIsDeleteOpen(true), testId: "agent-cap" })) }), jsx(TabsContent, { value: SPEND_CAPS_SUB_TABS.users, className: "space-y-4", children: isUsersForbidden ? (jsx(DeniedState, { labels: labels.denied })) : (jsx(UserCapsSection, { labels: labels, tenantKey: tenantKey, caps: userCaps, isLoading: isUserCapsLoading, isSaving: isSavingUserCap, isDeleting: isDeletingUserCap, onSave: saveUserCap, onDelete: removeUserCap })) })] }) }), jsx(Dialog, { open: isDeleteOpen, onOpenChange: () => setIsDeleteOpen(false), children: jsxs(DialogContent, { "aria-describedby": "spend-cap-delete-description", "data-testid": "spend-cap-delete-modal", children: [jsx(DialogHeader, { children: jsx(DialogTitle, { className: "ibl-dialog-title", children: labels.deleteModal.title }) }), jsx("div", { className: "my-5", id: "spend-cap-delete-description", children: jsx("p", { className: "text-sm text-[#646464]", children: labels.deleteModal.description }) }), jsxs(DialogFooter, { className: "gap-3", children: [jsx(Button$1, { variant: "outline", onClick: () => setIsDeleteOpen(false), children: labels.deleteModal.cancel }), jsx(Button$1, { className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", onClick: handleConfirmDelete, disabled: isDeletingAgentCap, "data-testid": "spend-cap-delete-confirm", children: isDeletingAgentCap ? labels.form.removing : labels.deleteModal.confirm })] })] }) })] }));
|
|
190231
|
+
}
|
|
190232
|
+
|
|
190233
|
+
function errorStatus$1(error) {
|
|
190234
|
+
return error === null || error === void 0 ? void 0 : error.status;
|
|
190235
|
+
}
|
|
190236
|
+
/**
|
|
190237
|
+
* Tenant-settings Billing "Agent Limits" tab: every configured agent spend
|
|
190238
|
+
* cap in the workspace from the tenant-wide list endpoint (no per-agent
|
|
190239
|
+
* fan-out), with an agent autocomplete filter, a direct status toggle per
|
|
190240
|
+
* row (datasets-table pattern) and a manage action that opens the agent's
|
|
190241
|
+
* full Billing editor (`AgentSpendCapsTab` — agent limit + per-user limits)
|
|
190242
|
+
* in a popup. Rendered inside `BillingTab`.
|
|
190243
|
+
*/
|
|
190244
|
+
function AgentLimitsSection({ tenantKey, username, labels: labelsOverride, }) {
|
|
190245
|
+
var _a;
|
|
190246
|
+
const t = useT();
|
|
190247
|
+
const labels = React__default.useMemo(() => resolveSpendCapsTabLabels(labelsOverride, t), [labelsOverride, t]);
|
|
190248
|
+
const [upsertAgentCap, { isLoading: isSavingCap }] = useUpsertAgentSpendCapMutation();
|
|
190249
|
+
// Agent autocomplete filter (also the entry point for setting a limit on
|
|
190250
|
+
// an agent that has none yet).
|
|
190251
|
+
const [agentFilter, setAgentFilter] = React__default.useState(null);
|
|
190252
|
+
const [agentSearch, setAgentSearch] = React__default.useState('');
|
|
190253
|
+
const [debouncedAgentSearch] = a$3(agentSearch, 300);
|
|
190254
|
+
const shouldSearchAgents = Boolean(tenantKey && debouncedAgentSearch.trim().length >= 2);
|
|
190255
|
+
const { data: mentorsResponse, isFetching: isSearchingAgents } = useGetMentorsQuery({ org: tenantKey, username, limit: 20, query: debouncedAgentSearch.trim() }, { skip: !shouldSearchAgents });
|
|
190256
|
+
const agentOptions = ((_a = mentorsResponse === null || mentorsResponse === void 0 ? void 0 : mentorsResponse.results) !== null && _a !== void 0 ? _a : [])
|
|
190257
|
+
.filter((mentor) => Boolean(mentor.unique_id))
|
|
190258
|
+
.map((mentor) => ({
|
|
190259
|
+
value: mentor.unique_id,
|
|
190260
|
+
label: mentor.name || mentor.unique_id,
|
|
190261
|
+
}));
|
|
190262
|
+
// The endpoint supports filtering by mentor server-side — pass it through
|
|
190263
|
+
// instead of fetching everything and filtering client-side.
|
|
190264
|
+
const { data: visibleCaps = [], error: capsError, isLoading, isFetching, } = useListAgentSpendCapsQuery({ org: tenantKey, ...(agentFilter ? { mentor: agentFilter.value } : {}) }, { skip: !tenantKey });
|
|
190265
|
+
const isForbidden = errorStatus$1(capsError) === 403;
|
|
190266
|
+
const [selectedAgent, setSelectedAgent] = React__default.useState(null);
|
|
190267
|
+
// Status toggle saves immediately (datasets-table pattern) — the PUT is an
|
|
190268
|
+
// upsert, so the cap's current fields ride along with the flipped flag.
|
|
190269
|
+
const toggleEnabled = async (cap, checked) => {
|
|
190270
|
+
var _a;
|
|
190271
|
+
try {
|
|
190272
|
+
await upsertAgentCap({
|
|
190273
|
+
org: tenantKey,
|
|
190274
|
+
mentorId: (_a = cap.mentor_unique_id) !== null && _a !== void 0 ? _a : '',
|
|
190275
|
+
formData: {
|
|
190276
|
+
interval_type: cap.interval_type,
|
|
190277
|
+
max_cost_usd: cap.max_cost_usd,
|
|
190278
|
+
enforcement: cap.enforcement,
|
|
190279
|
+
alert_thresholds: cap.alert_thresholds,
|
|
190280
|
+
enabled: checked,
|
|
190281
|
+
},
|
|
190282
|
+
}).unwrap();
|
|
190283
|
+
toast.success(labels.toasts.saved);
|
|
190284
|
+
}
|
|
190285
|
+
catch (error) {
|
|
190286
|
+
console.error('[AgentLimitsSection] toggle spend limit error', error);
|
|
190287
|
+
toast.error(labels.toasts.saveError);
|
|
190288
|
+
}
|
|
190289
|
+
};
|
|
190290
|
+
return (jsxs(Fragment$1, { children: [jsxs("div", { "data-testid": "spend-limits-agents-section", className: "rounded-lg border border-[#e5e5e5] bg-white shadow-[0_1px_2px_0_rgba(0,0,0,0.05)] dark:bg-zinc-900 dark:border-zinc-800", children: [jsx("div", { className: "flex flex-col gap-2 p-4 sm:flex-row sm:items-start sm:justify-between", children: jsxs("div", { className: "space-y-0.5", children: [jsx("h3", { className: "text-[16px] font-medium leading-6 text-[#101828] dark:text-gray-100", children: labels.tenantSections.agents.title }), jsx("p", { className: "text-[13px] font-normal leading-5 text-[#4a5565] dark:text-gray-400", children: labels.tenantSections.agents.description })] }) }), jsx("div", { className: "space-y-3 border-t border-[#e5e7eb] dark:border-zinc-800 px-4 py-3", children: 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": "agent-limits-denied", children: [jsx(ShieldAlert, { className: "h-6 w-6 text-gray-400" }), jsx("p", { className: "text-sm font-medium text-gray-900", children: labels.denied.title }), jsx("p", { className: "max-w-sm text-xs text-gray-600", children: labels.denied.description })] })) : (jsxs(Fragment$1, { children: [jsx("div", { className: "max-w-sm", children: jsx(SearchSelect, { labels: labels.tenantSections.agents.filter, selected: agentFilter, onSelect: setAgentFilter, onClear: () => setAgentFilter(null), searchTerm: agentSearch, onSearchTermChange: setAgentSearch, options: agentOptions, isLoading: isSearchingAgents, testId: "agent-limits-filter" }) }), jsx("div", { className: "overflow-hidden rounded-md border", "data-testid": "agent-limits-list", children: jsx("div", { className: "overflow-x-auto sm:mx-0", children: jsx("div", { className: "inline-block min-w-full align-middle", children: isLoading || isFetching ? (jsx("div", { className: "flex w-full items-center justify-center py-10", "data-testid": "agent-limits-loading", 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.tenantSections.agents.table.agentColumn }), jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: labels.tenantSections.agents.table.limitColumn }), jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: labels.tenantSections.agents.table.spentColumn }), jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: labels.tenantSections.agents.table.remainingColumn }), jsx(TableHead, { className: "p-3 text-left text-sm whitespace-nowrap text-[#646464]", children: labels.tenantSections.agents.table.statusColumn }), jsx(TableHead, { className: "w-10 p-3" })] }) }), jsx(TableBody, { children: visibleCaps.length === 0 ? (jsx(TableRow$1, { children: jsx(TableCell$1, { colSpan: 6, className: "p-4 text-center text-[#646464]", "data-testid": "agent-limits-empty", children: jsxs("div", { className: "space-y-2", children: [jsx("p", { children: agentFilter
|
|
190291
|
+
? labels.tenantSections.agents.emptyFiltered
|
|
190292
|
+
: labels.tenantSections.agents.emptyState }), agentFilter && (jsx(Button$1, { className: "ibl-button-primary", onClick: () => setSelectedAgent({
|
|
190293
|
+
uniqueId: agentFilter.value,
|
|
190294
|
+
name: agentFilter.label,
|
|
190295
|
+
}), "data-testid": "agent-limits-set-limit-button", children: labels.tenantSections.agents.setLimitButton }))] }) }) })) : (visibleCaps.map((cap) => {
|
|
190296
|
+
var _a;
|
|
190297
|
+
const uniqueId = (_a = cap.mentor_unique_id) !== null && _a !== void 0 ? _a : '';
|
|
190298
|
+
const name = cap.mentor_name || uniqueId;
|
|
190299
|
+
return (jsxs(TableRow$1, { className: "border-b last:border-0", "data-testid": `agent-limits-row-${uniqueId}`, children: [jsx(TableCell$1, { className: "max-w-[240px] truncate p-3 font-medium whitespace-nowrap text-[#646464]", children: name }), jsxs(TableCell$1, { className: "p-3 whitespace-nowrap text-[#646464]", children: [formatUsd$1(cap.max_cost_usd), " /", ' ', labels.form.intervalOptions[cap.interval_type]] }), jsx(TableCell$1, { className: "p-3 whitespace-nowrap text-[#646464]", children: formatUsd$1(cap.current_spend_usd) }), jsx(TableCell$1, { className: "p-3 whitespace-nowrap text-[#646464]", children: formatUsd$1(cap.remaining_usd) }), jsx(TableCell$1, { className: "p-3", children: jsxs("div", { className: "flex items-center gap-1.5", children: [jsx(CustomSwitch, { checked: cap.enabled, onCheckedChange: (checked) => toggleEnabled(cap, checked), disabled: isSavingCap, "aria-label": labels.tenantSections.agents.statusToggleAria(name), "data-testid": `agent-limits-status-switch-${uniqueId}` }), cap.is_exceeded && (jsx("span", { className: "rounded-full bg-red-100 px-2 py-0.5 text-[11px] font-medium text-red-700", children: labels.usage.exceededBadge })), cap.enforcement === 'alert_only' && (jsx("span", { className: "rounded-full bg-amber-100 px-2 py-0.5 text-[11px] font-medium text-amber-800", children: labels.usage.alertOnlyBadge }))] }) }), jsx(TableCell$1, { className: "p-3 text-right", children: jsx(Button$1, { type: "button", variant: "ghost", size: "icon", "aria-label": labels.tenantSections.agents.manageAria(name), onClick: () => setSelectedAgent({ uniqueId, name }), "data-testid": `agent-limits-manage-${uniqueId}`, children: jsx(SlidersHorizontal, { className: "h-4 w-4" }) }) })] }, cap.id));
|
|
190300
|
+
})) })] })) }) }) })] })) })] }), jsx(Dialog, { open: selectedAgent !== null, onOpenChange: () => setSelectedAgent(null), children: jsxs(DialogContent, { "aria-describedby": undefined, "data-testid": "agent-limits-modal",
|
|
190301
|
+
// Content-driven height (capped at 90vh) — the tab itself locks its
|
|
190302
|
+
// sub-tabs to a shared height so switching doesn't shift the dialog.
|
|
190303
|
+
className: "flex max-h-[90vh] w-[calc(100vw-2rem)] flex-col gap-0 overflow-hidden p-0 sm:max-w-4xl", children: [jsx(DialogHeader, { className: "flex-shrink-0 border-b border-gray-200 px-6 py-4 pr-12", children: jsx(DialogTitle, { className: "ibl-dialog-title", children: selectedAgent === null || selectedAgent === void 0 ? void 0 : selectedAgent.name }) }), jsx("div", { className: "flex min-h-0 flex-1 flex-col overflow-hidden", children: selectedAgent && (jsx(AgentSpendCapsTab, { tenantKey: tenantKey, mentorId: selectedAgent.uniqueId, headerClassName: "px-6", bodyClassName: "px-6 pb-6 lg:px-8" })) })] }) })] }));
|
|
190304
|
+
}
|
|
190305
|
+
|
|
190306
|
+
/**
|
|
190307
|
+
* Credit-balance → USD conversion for the Plan & Credits tab. The rate is
|
|
190308
|
+
* never hardcoded — it differs per environment — so everything here derives
|
|
190309
|
+
* from whichever fields the `/api/billing/account/` response actually
|
|
190310
|
+
* carries, and returns null when it carries none (the UI then shows credits
|
|
190311
|
+
* only).
|
|
190312
|
+
*/
|
|
190313
|
+
function toFiniteNumber(value) {
|
|
190314
|
+
if (value === null || value === undefined || value === '')
|
|
190315
|
+
return null;
|
|
190316
|
+
const num = typeof value === 'string' ? parseFloat(value) : value;
|
|
190317
|
+
return Number.isFinite(num) ? num : null;
|
|
190318
|
+
}
|
|
190319
|
+
/**
|
|
190320
|
+
* The environment's credits-per-USD rate read off the transaction history:
|
|
190321
|
+
* an `add` transaction records both the credits granted and the dollars paid
|
|
190322
|
+
* for them (`/api/billing/transactions/`), so their ratio IS the rate. Uses
|
|
190323
|
+
* the newest non-failed top-up (rows come most-recent first). Null when no
|
|
190324
|
+
* usable top-up exists yet.
|
|
190325
|
+
*/
|
|
190326
|
+
function resolveCreditsPerUsdFromTransactions(transactions) {
|
|
190327
|
+
for (const tx of transactions !== null && transactions !== void 0 ? transactions : []) {
|
|
190328
|
+
if (tx.transaction_type !== 'add' || tx.status === 'failed')
|
|
190329
|
+
continue;
|
|
190330
|
+
const usd = toFiniteNumber(tx.payment_amount_usd);
|
|
190331
|
+
const credits = toFiniteNumber(tx.credits_amount);
|
|
190332
|
+
if (usd !== null && usd > 0 && credits !== null && credits > 0)
|
|
190333
|
+
return credits / usd;
|
|
190334
|
+
}
|
|
190335
|
+
return null;
|
|
190336
|
+
}
|
|
190337
|
+
/**
|
|
190338
|
+
* The environment's credits-per-USD rate, from whichever field the backend
|
|
190339
|
+
* provides: `credits_per_usd` directly, `usd_per_credit` inverted, or — when
|
|
190340
|
+
* only the two balance figures exist — their ratio. Null when none resolve.
|
|
190341
|
+
*/
|
|
190342
|
+
function resolveCreditsPerUsd(billing) {
|
|
190343
|
+
const direct = toFiniteNumber(billing === null || billing === void 0 ? void 0 : billing.credits_per_usd);
|
|
190344
|
+
if (direct !== null && direct > 0)
|
|
190345
|
+
return direct;
|
|
190346
|
+
const usdPerCredit = toFiniteNumber(billing === null || billing === void 0 ? void 0 : billing.usd_per_credit);
|
|
190347
|
+
if (usdPerCredit !== null && usdPerCredit > 0)
|
|
190348
|
+
return 1 / usdPerCredit;
|
|
190349
|
+
const credits = toFiniteNumber(billing === null || billing === void 0 ? void 0 : billing.available_credits);
|
|
190350
|
+
const usd = toFiniteNumber(billing === null || billing === void 0 ? void 0 : billing.available_credits_usd);
|
|
190351
|
+
if (credits !== null && usd !== null && credits > 0 && usd > 0)
|
|
190352
|
+
return credits / usd;
|
|
190353
|
+
return null;
|
|
190354
|
+
}
|
|
190355
|
+
/**
|
|
190356
|
+
* Dollar value of the available credit balance: the backend's explicit
|
|
190357
|
+
* `available_credits_usd` when present, otherwise the balance divided by the
|
|
190358
|
+
* resolved rate. Null when the response supports neither.
|
|
190359
|
+
*/
|
|
190360
|
+
function resolveAvailableCreditsUsd(billing) {
|
|
190361
|
+
const explicit = toFiniteNumber(billing === null || billing === void 0 ? void 0 : billing.available_credits_usd);
|
|
190362
|
+
if (explicit !== null)
|
|
190363
|
+
return explicit;
|
|
190364
|
+
const rate = resolveCreditsPerUsd(billing);
|
|
190365
|
+
const credits = toFiniteNumber(billing === null || billing === void 0 ? void 0 : billing.available_credits);
|
|
190366
|
+
if (rate !== null && credits !== null)
|
|
190367
|
+
return credits / rate;
|
|
190368
|
+
return null;
|
|
190369
|
+
}
|
|
190370
|
+
/**
|
|
190371
|
+
* Format a credits-per-USD rate for the "X credits = $1" hint: whole rates
|
|
190372
|
+
* stay whole ("1,000"), fractional ones keep two decimals.
|
|
190373
|
+
*/
|
|
190374
|
+
function formatCreditsPerUsd(rate) {
|
|
190375
|
+
const rounded = Math.round(rate);
|
|
190376
|
+
const isWhole = Math.abs(rate - rounded) < 0.005;
|
|
190377
|
+
return isWhole
|
|
190378
|
+
? rounded.toLocaleString('en-US')
|
|
190379
|
+
: rate.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
|
190380
|
+
}
|
|
190381
|
+
|
|
188714
190382
|
function formatCredits(credits) {
|
|
188715
190383
|
if (credits === null || credits === undefined || Number.isNaN(credits))
|
|
188716
190384
|
return '0';
|
|
@@ -188745,17 +190413,17 @@ function formatUsd(value) {
|
|
|
188745
190413
|
return '—';
|
|
188746
190414
|
return `$${num.toFixed(2)}`;
|
|
188747
190415
|
}
|
|
188748
|
-
function Section({ title, description, action, children, testId }) {
|
|
188749
|
-
return (jsxs("div", { "data-testid": testId, className:
|
|
190416
|
+
function Section({ title, description, action, children, testId, className }) {
|
|
190417
|
+
return (jsxs("div", { "data-testid": testId, className: cn('rounded-lg border border-[#e5e5e5] bg-white shadow-[0_1px_2px_0_rgba(0,0,0,0.05)] dark:bg-zinc-900 dark:border-zinc-800', className), children: [jsxs("div", { className: "flex flex-col gap-2 p-4 sm:flex-row sm:items-start sm:justify-between", children: [jsxs("div", { className: "space-y-0.5", children: [jsx("h3", { className: "text-[16px] font-medium leading-6 text-[#101828] dark:text-gray-100", children: title }), description && (jsx("p", { className: "text-[13px] font-normal leading-5 text-[#4a5565] dark:text-gray-400", children: description }))] }), action && jsx("div", { className: "shrink-0", children: action })] }), children && (jsx("div", { className: "border-t border-[#e5e7eb] dark:border-zinc-800 px-4 py-3", children: children }))] }));
|
|
188750
190418
|
}
|
|
188751
|
-
function Stat({ label, value, hint }) {
|
|
188752
|
-
return (jsxs("div", { className: "space-y-0.5", children: [jsx("p", { className: "text-[11px] font-medium uppercase tracking-wide text-[#6a7282] dark:text-gray-400", children: label }), jsx("p", { className: "text-[18px] font-semibold leading-6 text-[#101828] dark:text-gray-100", children: value }), hint && (jsx("p", { className: "text-[12px] font-normal leading-4 text-[#6a7282] dark:text-gray-500", children: hint }))] }));
|
|
190419
|
+
function Stat({ label, value, hint, info }) {
|
|
190420
|
+
return (jsxs("div", { className: "space-y-0.5", children: [jsxs("div", { className: "flex items-center gap-0.5", children: [jsx("p", { className: "text-[11px] font-medium uppercase tracking-wide text-[#6a7282] dark:text-gray-400", children: label }), info && jsx(CardInfoIcon, { description: info, className: "-my-1" })] }), jsx("p", { className: "text-[18px] font-semibold leading-6 text-[#101828] dark:text-gray-100", children: value }), hint && (jsx("p", { className: "text-[12px] font-normal leading-4 text-[#6a7282] dark:text-gray-500", children: hint }))] }));
|
|
188753
190421
|
}
|
|
188754
190422
|
function SectionSkeleton({ withBody = false }) {
|
|
188755
190423
|
return (jsxs("div", { className: "rounded-lg border border-[#e5e5e5] bg-white shadow-[0_1px_2px_0_rgba(0,0,0,0.05)] dark:bg-zinc-900 dark:border-zinc-800", children: [jsxs("div", { className: "flex flex-col gap-2 p-4 sm:flex-row sm:items-start sm:justify-between", children: [jsxs("div", { className: "space-y-2", children: [jsx(Skeleton, { className: "h-5 w-32" }), jsx(Skeleton, { className: "h-4 w-56" })] }), jsx(Skeleton, { className: "h-9 w-32" })] }), withBody && (jsx("div", { className: "border-t border-[#e5e7eb] dark:border-zinc-800 px-4 py-3", children: jsx("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-3", children: [0, 1, 2].map((i) => (jsxs("div", { className: "space-y-2", children: [jsx(Skeleton, { className: "h-3 w-20" }), jsx(Skeleton, { className: "h-6 w-24" })] }, i))) }) }))] }));
|
|
188756
190424
|
}
|
|
188757
190425
|
function BillingTab({ tenant, userActiveApp = null, username, mainPlatformKey, currentUserEmail, redirectUrl, }) {
|
|
188758
|
-
var _a;
|
|
190426
|
+
var _a, _b, _c;
|
|
188759
190427
|
const t = useT();
|
|
188760
190428
|
const [isAutoRechargeModalOpen, setIsAutoRechargeModalOpen] = useState(false);
|
|
188761
190429
|
const [isAddCreditsModalOpen, setIsAddCreditsModalOpen] = useState(false);
|
|
@@ -188767,6 +190435,10 @@ function BillingTab({ tenant, userActiveApp = null, username, mainPlatformKey, c
|
|
|
188767
190435
|
currentUserEmail,
|
|
188768
190436
|
});
|
|
188769
190437
|
const { data: billingInfo, isLoading: isBillingInfoLoading } = useGetAccountBillingInfoQuery({ platform_key: tenant }, { refetchOnMountOrArgChange: true });
|
|
190438
|
+
// Top-up rows carry both the credits granted and the dollars paid — the
|
|
190439
|
+
// pair that reveals this environment's credits-per-USD rate for the
|
|
190440
|
+
// dollar-equivalent balance display.
|
|
190441
|
+
const { data: addTransactions } = useGetCreditTransactionsQuery({ platform_key: tenant, transaction_type: 'add' }, { skip: !tenant });
|
|
188770
190442
|
const [createStripePortal] = useCreateStripeCustomerPortalMutation();
|
|
188771
190443
|
const handleBilling = async ({ extraRequestBody = {}, }) => {
|
|
188772
190444
|
return await createStripePortal({
|
|
@@ -188802,7 +190474,7 @@ function BillingTab({ tenant, userActiveApp = null, username, mainPlatformKey, c
|
|
|
188802
190474
|
}
|
|
188803
190475
|
};
|
|
188804
190476
|
if (isBillingInfoLoading) {
|
|
188805
|
-
return (jsx("div", { className: "container max-w-
|
|
190477
|
+
return (jsx("div", { className: "container mx-auto max-w-7xl !mt-0", children: jsxs("div", { className: "space-y-4", children: [jsx(SectionSkeleton, {}), jsx(SectionSkeleton, { withBody: true }), jsx(SectionSkeleton, { withBody: true })] }) }));
|
|
188806
190478
|
}
|
|
188807
190479
|
const billing = billingInfo;
|
|
188808
190480
|
const isFreePlan = formatPlanLabel(billing === null || billing === void 0 ? void 0 : billing.current_plan) === 'Free' ||
|
|
@@ -188815,6 +190487,27 @@ function BillingTab({ tenant, userActiveApp = null, username, mainPlatformKey, c
|
|
|
188815
190487
|
: 'Premium';
|
|
188816
190488
|
const availableCredits = (_a = Number(billing === null || billing === void 0 ? void 0 : billing.available_credits)) !== null && _a !== void 0 ? _a : 0;
|
|
188817
190489
|
const consumedCredits = (billing === null || billing === void 0 ? void 0 : billing.consumed_credits) != null ? parseInt(billing.consumed_credits, 10) : null;
|
|
190490
|
+
// Recharging is priced in dollars, so the balance shows its dollar value
|
|
190491
|
+
// too — but only when the API provides the data for it (the rate differs
|
|
190492
|
+
// per environment and is never hardcoded). Preference order: explicit
|
|
190493
|
+
// account fields, then the credits/dollars pair on the newest top-up
|
|
190494
|
+
// transaction.
|
|
190495
|
+
const creditsPerUsd = (_b = resolveCreditsPerUsd(billing)) !== null && _b !== void 0 ? _b : resolveCreditsPerUsdFromTransactions(addTransactions);
|
|
190496
|
+
const availableCreditsUsd = (_c = resolveAvailableCreditsUsd(billing)) !== null && _c !== void 0 ? _c : (creditsPerUsd != null && Number.isFinite(availableCredits)
|
|
190497
|
+
? availableCredits / creditsPerUsd
|
|
190498
|
+
: null);
|
|
190499
|
+
const availableCreditsHint = availableCreditsUsd != null
|
|
190500
|
+
? [
|
|
190501
|
+
t('profileBilling.creditsUsdEquivalent', { amount: formatUsd(availableCreditsUsd) }),
|
|
190502
|
+
creditsPerUsd != null
|
|
190503
|
+
? t('profileBilling.creditsConversionHint', {
|
|
190504
|
+
credits: formatCreditsPerUsd(creditsPerUsd),
|
|
190505
|
+
})
|
|
190506
|
+
: null,
|
|
190507
|
+
]
|
|
190508
|
+
.filter(Boolean)
|
|
190509
|
+
.join(' · ')
|
|
190510
|
+
: undefined;
|
|
188818
190511
|
const hasPaymentMethod = Boolean(billing === null || billing === void 0 ? void 0 : billing.has_payment_method);
|
|
188819
190512
|
const isAutoRechargeEnabled = Boolean(billing === null || billing === void 0 ? void 0 : billing.auto_recharge_enabled);
|
|
188820
190513
|
const spendingLimitRaw = billing === null || billing === void 0 ? void 0 : billing.auto_recharge_spending_limit_usd;
|
|
@@ -188838,21 +190531,21 @@ function BillingTab({ tenant, userActiveApp = null, username, mainPlatformKey, c
|
|
|
188838
190531
|
}
|
|
188839
190532
|
return t('profileBilling.manageSubscriptionAndBilling');
|
|
188840
190533
|
})();
|
|
188841
|
-
return (jsxs("div", { className: "container mx-auto max-w-
|
|
188842
|
-
|
|
188843
|
-
|
|
188844
|
-
|
|
188845
|
-
|
|
188846
|
-
|
|
188847
|
-
|
|
188848
|
-
|
|
188849
|
-
|
|
188850
|
-
|
|
188851
|
-
|
|
188852
|
-
|
|
188853
|
-
|
|
188854
|
-
|
|
188855
|
-
|
|
190534
|
+
return (jsxs("div", { className: "container mx-auto max-w-7xl !mt-0", children: [jsxs(Tabs, { defaultValue: "plan", "aria-label": t('profileBilling.billingTabsAriaLabel'), children: [jsxs(TabsList, { "aria-label": t('profileBilling.billingTabsAriaLabel'), className: "max-w-full overflow-x-auto justify-start px-1", children: [jsx(TabsTrigger, { value: "plan", children: t('profileBilling.tabPlanAndCredits') }), jsx(TabsTrigger, { value: "spend-limits", children: t('profileBilling.tabSpendLimits') }), jsx(TabsTrigger, { value: "agent-limits", children: t('profileBilling.tabAgentLimits') })] }), jsx(TabsContent, { className: "mt-4", value: "plan", children: jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "grid grid-cols-1 items-start gap-4 lg:grid-cols-3", children: [jsx(Section, { title: t('profileBilling.planSectionTitle'), testId: "billing-plan-section", description: planDescription, className: "lg:col-span-1 lg:h-full", action: isFreePlan || isTrialPlan ? (jsxs(Button$1, { onClick: () => handleUpgrade('premium'), disabled: isUpgrading, className: "h-9 px-4 text-[14px] font-medium text-white bg-gradient-to-r from-[rgb(56,161,229)] to-[rgb(114,132,255)] hover:opacity-90 disabled:opacity-70", children: [isUpgrading ? (jsx(LoaderCircle, { className: "h-4 w-4 animate-spin" })) : (jsx(Sparkles, { className: "h-4 w-4" })), isUpgrading ? t('profileBilling.redirecting') : t('profileBilling.upgrade')] })) : undefined, children: jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { "data-testid": "billing-plan-label", className: "text-[18px] font-semibold leading-7 text-[#101828] dark:text-gray-100", children: planLabel }), jsx("span", { className: "rounded-full px-2.5 py-0.5 text-[12px] font-medium", style: {
|
|
190535
|
+
backgroundColor: '#f5f8ff',
|
|
190536
|
+
color: '#155dfc',
|
|
190537
|
+
border: '1px solid #d0e0ff',
|
|
190538
|
+
}, children: t('profileBilling.currentBadge') })] }) }), jsx(Section, { title: t('profileBilling.creditsSectionTitle'), testId: "billing-credits-section", description: t('profileBilling.creditsDescription'), className: "lg:col-span-2 lg:h-full", action: isFreePlan || isTrialPlan ? undefined : hasPaymentMethod ? (jsx(Button$1, { variant: "outline", onClick: () => setIsAddCreditsModalOpen(true), className: "h-9 px-4 text-[14px] font-medium", children: t('profileBilling.addCredits') })) : (jsxs(Button$1, { onClick: handleSetupPaymentMethod, disabled: isRedirectingToPaymentSettings, className: "h-9 px-4 text-[14px] font-medium bg-gradient-to-r from-[#2563EB] to-[#93C5FD] hover:opacity-90 text-white", children: [isRedirectingToPaymentSettings ? (jsx(LoaderCircle, { className: "h-4 w-4 animate-spin" })) : (jsx(Wallet, { className: "h-4 w-4" })), isRedirectingToPaymentSettings
|
|
190539
|
+
? t('profileBilling.redirecting')
|
|
190540
|
+
: t('profileBilling.manageBilling')] })), children: jsxs("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-3", children: [jsx(Stat, { label: t('profileBilling.availableLabel'), value: t('profileBilling.creditsUnit', { amount: formatCredits(availableCredits) }), hint: availableCreditsHint }), consumedCredits != null && (jsx(Stat, { label: t('profileBilling.consumedLabel'), value: t('profileBilling.creditsUnit', { amount: formatCredits(consumedCredits) }), info: t('profileBilling.usedThisPeriod') })), (billing === null || billing === void 0 ? void 0 : billing.credits_reset_date) && (jsx(Stat, { label: t('profileBilling.resetsOnLabel'), value: formatRenewalDate(billing.credits_reset_date), info: t('profileBilling.nextResetDate') }))] }) })] }), !isFreePlan && !isTrialPlan && hasPaymentMethod && (jsx(Section, { title: t('profileBilling.autoRechargeSectionTitle'), testId: "billing-auto-recharge-section", description: t('profileBilling.autoRechargeDescription'), action: jsxs("div", { className: "flex items-center gap-3", children: [jsx("span", { "data-testid": "billing-auto-recharge-status", className: "rounded-full px-2.5 py-0.5 text-[12px] font-medium", style: {
|
|
190541
|
+
backgroundColor: isAutoRechargeEnabled ? '#f5f8ff' : '#f3f4f6',
|
|
190542
|
+
color: isAutoRechargeEnabled ? '#155dfc' : '#6a7282',
|
|
190543
|
+
border: `1px solid ${isAutoRechargeEnabled ? '#d0e0ff' : '#e5e7eb'}`,
|
|
190544
|
+
}, children: isAutoRechargeEnabled
|
|
190545
|
+
? t('profileBilling.enabled')
|
|
190546
|
+
: t('profileBilling.disabled') }), jsx(Button$1, { variant: "outline", onClick: () => setIsAutoRechargeModalOpen(true), className: "h-9 px-4 text-[14px] font-medium", children: t('profileBilling.manageUsage') })] }), children: isAutoRechargeEnabled ? (jsxs("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-3", children: [jsx(Stat, { label: t('profileBilling.thresholdLabel'), value: formatUsd(billing === null || billing === void 0 ? void 0 : billing.auto_recharge_threshold_usd), info: t('profileBilling.thresholdHint') }), jsx(Stat, { label: t('profileBilling.rechargeAmountLabel'), value: formatUsd(billing === null || billing === void 0 ? void 0 : billing.auto_recharge_amount_usd), info: t('profileBilling.rechargeAmountHint') }), jsx(Stat, { label: t('profileBilling.spendingLimitLabel'), value: isUnlimitedSpendingLimit
|
|
190547
|
+
? t('profileBilling.unlimited')
|
|
190548
|
+
: formatUsd(spendingLimitRaw), info: t('profileBilling.spendingLimitHint') })] })) : (jsx("p", { className: "text-[14px] font-normal leading-5 text-[#4a5565] dark:text-gray-400", children: t('profileBilling.autoRechargeDisabledMessage') })) }))] }) }), jsx(TabsContent, { className: "mt-4", value: "spend-limits", children: jsx(SpendLimitsSection, { tenantKey: tenant }) }), jsx(TabsContent, { className: "mt-4", value: "agent-limits", children: jsx(AgentLimitsSection, { tenantKey: tenant, username: username }) })] }), jsx(AutoRechargeModal, { tenant: tenant, isOpen: isAutoRechargeModalOpen, onClose: () => setIsAutoRechargeModalOpen(false) }), jsx(AddCreditsModal, { tenant: tenant, isOpen: isAddCreditsModalOpen, onClose: () => setIsAddCreditsModalOpen(false) })] }));
|
|
188856
190549
|
}
|
|
188857
190550
|
|
|
188858
190551
|
const SmtpContent = ({ platformKey }) => {
|
|
@@ -189107,23 +190800,6 @@ const PlatformMembershipContent = ({ platformKey, authURL, }) => {
|
|
|
189107
190800
|
return (jsxs("div", { className: "flex items-center justify-between rounded-lg border px-6 py-6", style: { borderColor: 'oklch(.922 0 0)' }, children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: "text-sm font-medium text-[#646464]", children: t('advancedPlatformMembership.publicRegistrationLabel') }), jsx(TooltipProvider, { children: jsxs(Tooltip$1, { children: [jsx(TooltipTrigger, { "aria-label": t('advancedPlatformMembership.infoAriaLabel'), className: "hidden sm:block", children: jsx(Info$3, { className: "h-4 w-4 text-gray-400" }) }), jsx(TooltipContent, { className: "rounded-lg bg-gray-700 px-3 py-2 text-sm font-medium whitespace-nowrap text-white shadow-sm transition-opacity duration-300 z-50", children: jsx("p", { children: t('advancedPlatformMembership.infoTooltip') }) })] }) }), (membershipData === null || membershipData === void 0 ? void 0 : membershipData.allow_self_linking) && (jsx(TooltipProvider, { children: jsxs(Tooltip$1, { children: [jsx(TooltipTrigger, { onClick: handleCopyToClipboard, "aria-label": t('advancedPlatformMembership.copyAriaLabel'), className: "cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800 p-1 rounded transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-1", children: jsx(Copy, { className: "h-4 w-4 text-gray-400 hover:text-blue-500" }) }), jsx(TooltipContent, { className: "rounded-lg bg-gray-700 px-3 py-2 text-sm font-medium whitespace-nowrap text-white shadow-sm transition-opacity duration-300 z-50", children: jsx("p", { children: t('advancedPlatformMembership.copyTooltip') }) })] }) }))] }), jsxs("div", { className: "flex items-center gap-2", children: [jsx(Switch, { checked: membershipData === null || membershipData === void 0 ? void 0 : membershipData.allow_self_linking, onCheckedChange: handleToggleMembership, disabled: isUpdating, "aria-label": `${t('advancedPlatformMembership.switchAriaLabel')} ${(membershipData === null || membershipData === void 0 ? void 0 : membershipData.allow_self_linking) ? t('advancedPlatformMembership.switchEnabled') : t('advancedPlatformMembership.switchDisabled')}`, className: "cursor-pointer data-[state=checked]:bg-blue-500" }), isUpdating && (jsx("div", { className: "w-4 h-4 border-2 border-blue-500 border-t-transparent rounded-full animate-spin" }))] })] }));
|
|
189108
190801
|
};
|
|
189109
190802
|
|
|
189110
|
-
const Table$1 = React.forwardRef(({ className, ...props }, ref) => (jsx("div", { className: "relative w-full overflow-auto", children: jsx("table", { ref: ref, className: cn("w-full caption-bottom text-sm", className), ...props }) })));
|
|
189111
|
-
Table$1.displayName = "Table";
|
|
189112
|
-
const TableHeader$1 = React.forwardRef(({ className, ...props }, ref) => (jsx("thead", { ref: ref, className: cn("[&_tr]:border-b", className), ...props })));
|
|
189113
|
-
TableHeader$1.displayName = "TableHeader";
|
|
189114
|
-
const TableBody = React.forwardRef(({ className, ...props }, ref) => (jsx("tbody", { ref: ref, className: cn("[&_tr:last-child]:border-0", className), ...props })));
|
|
189115
|
-
TableBody.displayName = "TableBody";
|
|
189116
|
-
const TableFooter = React.forwardRef(({ className, ...props }, ref) => (jsx("tfoot", { ref: ref, className: cn("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", className), ...props })));
|
|
189117
|
-
TableFooter.displayName = "TableFooter";
|
|
189118
|
-
const TableRow$1 = React.forwardRef(({ className, ...props }, ref) => (jsx("tr", { ref: ref, className: cn("border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted", className), ...props })));
|
|
189119
|
-
TableRow$1.displayName = "TableRow";
|
|
189120
|
-
const TableHead = React.forwardRef(({ className, ...props }, ref) => (jsx("th", { ref: ref, className: cn("h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", className), ...props })));
|
|
189121
|
-
TableHead.displayName = "TableHead";
|
|
189122
|
-
const TableCell$1 = React.forwardRef(({ className, ...props }, ref) => (jsx("td", { ref: ref, className: cn("p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", className), ...props })));
|
|
189123
|
-
TableCell$1.displayName = "TableCell";
|
|
189124
|
-
const TableCaption = React.forwardRef(({ className, ...props }, ref) => (jsx("caption", { ref: ref, className: cn("mt-4 text-sm text-muted-foreground", className), ...props })));
|
|
189125
|
-
TableCaption.displayName = "TableCaption";
|
|
189126
|
-
|
|
189127
190803
|
const normalizeCustomDomains = (response) => {
|
|
189128
190804
|
if (!response) {
|
|
189129
190805
|
return [];
|
|
@@ -205059,15 +206735,15 @@ var mac$2 = typeof navigator != "undefined" && /Mac/.test(navigator.platform);
|
|
|
205059
206735
|
var ie$1 = typeof navigator != "undefined" && /MSIE \d|Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(navigator.userAgent);
|
|
205060
206736
|
|
|
205061
206737
|
// Fill in the digit keys
|
|
205062
|
-
for (var i$
|
|
206738
|
+
for (var i$8 = 0; i$8 < 10; i$8++) base[48 + i$8] = base[96 + i$8] = String(i$8);
|
|
205063
206739
|
|
|
205064
206740
|
// The function keys
|
|
205065
|
-
for (var i$
|
|
206741
|
+
for (var i$8 = 1; i$8 <= 24; i$8++) base[i$8 + 111] = "F" + i$8;
|
|
205066
206742
|
|
|
205067
206743
|
// And the alphabetic keys
|
|
205068
|
-
for (var i$
|
|
205069
|
-
base[i$
|
|
205070
|
-
shift[i$
|
|
206744
|
+
for (var i$8 = 65; i$8 <= 90; i$8++) {
|
|
206745
|
+
base[i$8] = String.fromCharCode(i$8 + 32);
|
|
206746
|
+
shift[i$8] = String.fromCharCode(i$8);
|
|
205071
206747
|
}
|
|
205072
206748
|
|
|
205073
206749
|
// For each code that doesn't have a shift-equivalent, copy the base name
|
|
@@ -219117,8 +220793,6 @@ function useAgentSearch({ tenantKey, query, limit = 6, offset, starred, featured
|
|
|
219117
220793
|
};
|
|
219118
220794
|
}
|
|
219119
220795
|
|
|
219120
|
-
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]}
|
|
219121
|
-
|
|
219122
220796
|
/**
|
|
219123
220797
|
* Higher-level agent search hook that manages search state, debounce, and
|
|
219124
220798
|
* pagination controls on top of `useAgentSearch`.
|
|
@@ -255061,37 +256735,46 @@ const EMPTY_SKILL_FORM = {
|
|
|
255061
256735
|
};
|
|
255062
256736
|
/** Page size for both skill lists. */
|
|
255063
256737
|
const SKILLS_PAGE_SIZE = 10;
|
|
255064
|
-
// Resolving the agent's own set (assignment metadata + its private skills)
|
|
255065
|
-
// needs a catalog lookup that the API cannot scope server-side yet (there is
|
|
255066
|
-
// no `mentor=` filter), so one bounded background fetch covers the join.
|
|
255067
|
-
const FULL_LIST_LIMIT = 1000;
|
|
255068
256738
|
function AgentSkills({ platformKey, mentorUniqueId }) {
|
|
255069
|
-
var _a, _b, _c, _d;
|
|
256739
|
+
var _a, _b, _c, _d, _e, _f;
|
|
255070
256740
|
const t = useT();
|
|
255071
256741
|
// Skills are managed independently of the sandbox connection — org-level
|
|
255072
256742
|
// skills and the mentor's assignments both resolve without a wired Claw
|
|
255073
256743
|
// instance, so we always fetch and let admins add/assign skills freely.
|
|
255074
|
-
|
|
256744
|
+
// The "Agent Skills" tab pages the assignment list server-side.
|
|
256745
|
+
const [agentPage, setAgentPage] = useState(1);
|
|
256746
|
+
const { data: assignmentsPage, isLoading: isLoadingAssignments } = useGetMentorSkillAssignmentsQuery({
|
|
255075
256747
|
org: platformKey,
|
|
255076
|
-
|
|
255077
|
-
|
|
256748
|
+
mentorUniqueId,
|
|
256749
|
+
limit: SKILLS_PAGE_SIZE,
|
|
256750
|
+
offset: (agentPage - 1) * SKILLS_PAGE_SIZE,
|
|
255078
256751
|
});
|
|
255079
|
-
const
|
|
256752
|
+
const agentAssignments = (_a = assignmentsPage === null || assignmentsPage === void 0 ? void 0 : assignmentsPage.results) !== null && _a !== void 0 ? _a : [];
|
|
256753
|
+
const agentTotalCount = (_b = assignmentsPage === null || assignmentsPage === void 0 ? void 0 : assignmentsPage.count) !== null && _b !== void 0 ? _b : 0;
|
|
256754
|
+
const agentTotalPages = Math.max(1, Math.ceil(agentTotalCount / SKILLS_PAGE_SIZE));
|
|
256755
|
+
useEffect(() => {
|
|
256756
|
+
if (agentPage > agentTotalPages)
|
|
256757
|
+
setAgentPage(agentTotalPages);
|
|
256758
|
+
}, [agentPage, agentTotalPages]);
|
|
256759
|
+
// Unpaged lookup of the same (per-agent, naturally small) assignment list —
|
|
256760
|
+
// powers the "Added" chips on the catalog tab, since the API has no
|
|
256761
|
+
// per-skill "is assigned" check to ask instead.
|
|
256762
|
+
const { data: assignmentCoverage } = useGetMentorSkillAssignmentsQuery({
|
|
255080
256763
|
org: platformKey,
|
|
255081
256764
|
mentorUniqueId,
|
|
255082
|
-
limit: FULL_LIST_LIMIT,
|
|
255083
|
-
offset: 0,
|
|
255084
256765
|
});
|
|
255085
|
-
|
|
256766
|
+
const assignedSkillIds = new Set(((_c = assignmentCoverage === null || assignmentCoverage === void 0 ? void 0 : assignmentCoverage.results) !== null && _c !== void 0 ? _c : []).map((a) => a.skill));
|
|
256767
|
+
// The "Available skills" tab pages the catalog server-side. Deliberately
|
|
256768
|
+
// unfiltered: disabled skills stay listed (switch off) so admins can
|
|
256769
|
+
// re-enable, edit or delete them.
|
|
255086
256770
|
const [catalogPage, setCatalogPage] = useState(1);
|
|
255087
256771
|
const { data: catalogSkillsPage, isLoading: isLoadingCatalog } = useGetAgentSkillsQuery({
|
|
255088
256772
|
org: platformKey,
|
|
255089
|
-
enabled: true,
|
|
255090
256773
|
limit: SKILLS_PAGE_SIZE,
|
|
255091
256774
|
offset: (catalogPage - 1) * SKILLS_PAGE_SIZE,
|
|
255092
256775
|
});
|
|
255093
|
-
const catalogSkills = (
|
|
255094
|
-
const catalogTotalCount = (
|
|
256776
|
+
const catalogSkills = (_d = catalogSkillsPage === null || catalogSkillsPage === void 0 ? void 0 : catalogSkillsPage.results) !== null && _d !== void 0 ? _d : [];
|
|
256777
|
+
const catalogTotalCount = (_e = catalogSkillsPage === null || catalogSkillsPage === void 0 ? void 0 : catalogSkillsPage.count) !== null && _e !== void 0 ? _e : 0;
|
|
255095
256778
|
const catalogTotalPages = Math.max(1, Math.ceil(catalogTotalCount / SKILLS_PAGE_SIZE));
|
|
255096
256779
|
useEffect(() => {
|
|
255097
256780
|
if (catalogPage > catalogTotalPages)
|
|
@@ -255109,25 +256792,11 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
|
|
|
255109
256792
|
const [editingSkill, setEditingSkill] = useState(null);
|
|
255110
256793
|
const [deletingSkill, setDeletingSkill] = useState(null);
|
|
255111
256794
|
const [form, setForm] = useState(EMPTY_SKILL_FORM);
|
|
255112
|
-
const
|
|
255113
|
-
|
|
255114
|
-
|
|
255115
|
-
|
|
255116
|
-
const
|
|
255117
|
-
const effectiveSlugs = new Set(effectiveSkills.map((s) => s.slug));
|
|
255118
|
-
// The "Agent skills" tab pages the (small, per-agent) effective set locally.
|
|
255119
|
-
const [agentPage, setAgentPage] = useState(1);
|
|
255120
|
-
const agentTotalPages = Math.max(1, Math.ceil(effectiveSkills.length / SKILLS_PAGE_SIZE));
|
|
255121
|
-
useEffect(() => {
|
|
255122
|
-
if (agentPage > agentTotalPages)
|
|
255123
|
-
setAgentPage(agentTotalPages);
|
|
255124
|
-
}, [agentPage, agentTotalPages]);
|
|
255125
|
-
const pagedEffectiveSkills = effectiveSkills.slice((agentPage - 1) * SKILLS_PAGE_SIZE, agentPage * SKILLS_PAGE_SIZE);
|
|
255126
|
-
// Catalog rows already covered by the agent's set (attached, or shadowed by
|
|
255127
|
-
// a same-slug private skill) show an "Added" chip instead of an Add button
|
|
255128
|
-
// — hiding them would make server pages uneven. Another agent's private
|
|
255129
|
-
// skills are not attachable at all.
|
|
255130
|
-
const isCoveredByAgent = (skill) => effectiveUniqueIds.has(skill.unique_id) || effectiveSlugs.has(skill.slug);
|
|
256795
|
+
const isMutatingAgentSkill = isCreatingAssignment || isUpdatingAssignment || isDeletingAssignment;
|
|
256796
|
+
// Catalog rows already assigned to the agent show an "Added" chip instead
|
|
256797
|
+
// of an Add button — hiding them would make server pages uneven. Another
|
|
256798
|
+
// agent's private skills get a disabled Add with an explanatory tooltip.
|
|
256799
|
+
const isCoveredByAgent = (skill) => assignedSkillIds.has(skill.unique_id);
|
|
255131
256800
|
const isOtherAgentsPrivate = (skill) => !!skill.mentor && skill.mentor !== mentorUniqueId;
|
|
255132
256801
|
// Featured skills from the `main` platform are read-only for tenants — the
|
|
255133
256802
|
// API 404s writes against them, so hide the edit/delete affordances.
|
|
@@ -255137,29 +256806,20 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
|
|
|
255137
256806
|
return skill.platform_key ? skill.platform_key === platformKey : !skill.is_featured;
|
|
255138
256807
|
};
|
|
255139
256808
|
const resetForm = () => setForm(EMPTY_SKILL_FORM);
|
|
255140
|
-
const
|
|
256809
|
+
const handleToggleAssignment = async (assignment, shouldEnable) => {
|
|
255141
256810
|
try {
|
|
255142
|
-
|
|
255143
|
-
|
|
255144
|
-
|
|
255145
|
-
|
|
255146
|
-
|
|
255147
|
-
|
|
255148
|
-
}
|
|
255149
|
-
else if (effective.assignmentId !== undefined) {
|
|
255150
|
-
await updateAssignment({
|
|
255151
|
-
org: platformKey,
|
|
255152
|
-
mentorUniqueId,
|
|
255153
|
-
assignmentPk: effective.assignmentId,
|
|
255154
|
-
enabled: shouldEnable,
|
|
255155
|
-
}).unwrap();
|
|
255156
|
-
}
|
|
256811
|
+
await updateAssignment({
|
|
256812
|
+
org: platformKey,
|
|
256813
|
+
mentorUniqueId,
|
|
256814
|
+
assignmentPk: assignment.id,
|
|
256815
|
+
enabled: shouldEnable,
|
|
256816
|
+
}).unwrap();
|
|
255157
256817
|
toast.success(t(shouldEnable
|
|
255158
256818
|
? 'clawSandboxAgentSkills.skillEnabled'
|
|
255159
|
-
: 'clawSandboxAgentSkills.skillDisabled', { name:
|
|
256819
|
+
: 'clawSandboxAgentSkills.skillDisabled', { name: assignment.skill_name }));
|
|
255160
256820
|
}
|
|
255161
256821
|
catch (_a) {
|
|
255162
|
-
toast.error(t('clawSandboxAgentSkills.failedToUpdateSkill', { name:
|
|
256822
|
+
toast.error(t('clawSandboxAgentSkills.failedToUpdateSkill', { name: assignment.skill_name }));
|
|
255163
256823
|
}
|
|
255164
256824
|
};
|
|
255165
256825
|
const handleAttach = async (skill) => {
|
|
@@ -255176,19 +256836,30 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
|
|
|
255176
256836
|
toast.error(t('clawSandboxAgentSkills.failedToUpdateSkill', { name: skill.name }));
|
|
255177
256837
|
}
|
|
255178
256838
|
};
|
|
255179
|
-
|
|
255180
|
-
|
|
255181
|
-
|
|
256839
|
+
// Platform-level enable/disable of the skill itself (catalog rows) — the
|
|
256840
|
+
// agent-tab switches PATCH the assignment instead.
|
|
256841
|
+
const handleToggleSkill = async (skill, shouldEnable) => {
|
|
256842
|
+
try {
|
|
256843
|
+
await updateSkill({ org: platformKey, id: skill.id, enabled: shouldEnable }).unwrap();
|
|
256844
|
+
toast.success(t(shouldEnable
|
|
256845
|
+
? 'clawSandboxAgentSkills.skillEnabled'
|
|
256846
|
+
: 'clawSandboxAgentSkills.skillDisabled', { name: skill.name }));
|
|
256847
|
+
}
|
|
256848
|
+
catch (_a) {
|
|
256849
|
+
toast.error(t('clawSandboxAgentSkills.failedToUpdateSkill', { name: skill.name }));
|
|
256850
|
+
}
|
|
256851
|
+
};
|
|
256852
|
+
const handleDetach = async (assignment) => {
|
|
255182
256853
|
try {
|
|
255183
256854
|
await deleteAssignment({
|
|
255184
256855
|
org: platformKey,
|
|
255185
256856
|
mentorUniqueId,
|
|
255186
|
-
assignmentPk:
|
|
256857
|
+
assignmentPk: assignment.id,
|
|
255187
256858
|
}).unwrap();
|
|
255188
|
-
toast.success(t('clawSandboxAgentSkills.skillRemoved', { name:
|
|
256859
|
+
toast.success(t('clawSandboxAgentSkills.skillRemoved', { name: assignment.skill_name }));
|
|
255189
256860
|
}
|
|
255190
256861
|
catch (_a) {
|
|
255191
|
-
toast.error(t('clawSandboxAgentSkills.failedToUpdateSkill', { name:
|
|
256862
|
+
toast.error(t('clawSandboxAgentSkills.failedToUpdateSkill', { name: assignment.skill_name }));
|
|
255192
256863
|
}
|
|
255193
256864
|
};
|
|
255194
256865
|
const openNewSkillDialog = () => {
|
|
@@ -255219,7 +256890,7 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
|
|
|
255219
256890
|
return;
|
|
255220
256891
|
}
|
|
255221
256892
|
try {
|
|
255222
|
-
await createSkill({
|
|
256893
|
+
const created = await createSkill({
|
|
255223
256894
|
org: platformKey,
|
|
255224
256895
|
name: form.name,
|
|
255225
256896
|
slug: form.slug,
|
|
@@ -255230,6 +256901,16 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
|
|
|
255230
256901
|
instruction: form.instruction,
|
|
255231
256902
|
enabled: true,
|
|
255232
256903
|
}).unwrap();
|
|
256904
|
+
// A private skill only takes effect once assigned, and the Agent Skills
|
|
256905
|
+
// tab lists assignments — so attach it to this agent right away.
|
|
256906
|
+
if (form.privateToAgent && (created === null || created === void 0 ? void 0 : created.unique_id)) {
|
|
256907
|
+
await createAssignment({
|
|
256908
|
+
org: platformKey,
|
|
256909
|
+
mentorUniqueId,
|
|
256910
|
+
skill: created.unique_id,
|
|
256911
|
+
enabled: true,
|
|
256912
|
+
}).unwrap();
|
|
256913
|
+
}
|
|
255233
256914
|
toast.success(t('clawSandboxAgentSkills.skillCreated'));
|
|
255234
256915
|
closeAllDialogs();
|
|
255235
256916
|
}
|
|
@@ -255244,6 +256925,10 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
|
|
|
255244
256925
|
toast.error(t('clawSandboxAgentSkills.nameAndSlugRequired'));
|
|
255245
256926
|
return;
|
|
255246
256927
|
}
|
|
256928
|
+
// A skill private to a different agent keeps its owner: the "Only This
|
|
256929
|
+
// Agent" toggle is locked in the form, and the PATCH omits `mentor` so a
|
|
256930
|
+
// save here can never silently un-private (or steal) it.
|
|
256931
|
+
const privateLocked = isOtherAgentsPrivate(editingSkill);
|
|
255247
256932
|
try {
|
|
255248
256933
|
await updateSkill({
|
|
255249
256934
|
org: platformKey,
|
|
@@ -255254,8 +256939,18 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
|
|
|
255254
256939
|
version: form.version,
|
|
255255
256940
|
category: form.category,
|
|
255256
256941
|
instruction: form.instruction,
|
|
255257
|
-
mentor: form.privateToAgent ? mentorUniqueId : null,
|
|
256942
|
+
...(!privateLocked && { mentor: form.privateToAgent ? mentorUniqueId : null }),
|
|
255258
256943
|
}).unwrap();
|
|
256944
|
+
// Making a skill private to this agent implies it should be usable by
|
|
256945
|
+
// it — attach it if it isn't assigned yet.
|
|
256946
|
+
if (!privateLocked && form.privateToAgent && !assignedSkillIds.has(editingSkill.unique_id)) {
|
|
256947
|
+
await createAssignment({
|
|
256948
|
+
org: platformKey,
|
|
256949
|
+
mentorUniqueId,
|
|
256950
|
+
skill: editingSkill.unique_id,
|
|
256951
|
+
enabled: true,
|
|
256952
|
+
}).unwrap();
|
|
256953
|
+
}
|
|
255259
256954
|
toast.success(t('clawSandboxAgentSkills.skillUpdated'));
|
|
255260
256955
|
closeAllDialogs();
|
|
255261
256956
|
}
|
|
@@ -255275,46 +256970,40 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
|
|
|
255275
256970
|
toast.error(t('clawSandboxAgentSkills.failedToDeleteSkill'));
|
|
255276
256971
|
}
|
|
255277
256972
|
};
|
|
255278
|
-
const renderBadges = (skill,
|
|
255279
|
-
|
|
255280
|
-
|
|
255281
|
-
|
|
255282
|
-
|
|
255283
|
-
|
|
255284
|
-
};
|
|
255285
|
-
const renderSkillMenu = (skill, effective) => {
|
|
255286
|
-
var _a, _b;
|
|
255287
|
-
const editable = isSkillEditable(skill);
|
|
255288
|
-
const showEdit = editable;
|
|
255289
|
-
const showDelete = editable;
|
|
255290
|
-
const detachable = (effective === null || effective === void 0 ? void 0 : effective.origin) === 'assignment';
|
|
255291
|
-
if (!showEdit && !showDelete && !detachable)
|
|
256973
|
+
const renderBadges = (skill) => (jsxs("span", { className: "flex flex-wrap items-center gap-1", children: [skill.category && (jsx("span", { className: "rounded-full bg-gray-100 px-2 py-0.5 text-[0.625rem] text-gray-500", children: skill.category })), skill.skill_type === 'native' && (jsx("span", { className: "rounded-full bg-purple-50 px-2 py-0.5 text-[0.625rem] text-purple-600", children: t('clawSandboxAgentSkills.nativeBadge') })), skill.is_featured && (jsx("span", { className: "rounded-full bg-amber-50 px-2 py-0.5 text-[0.625rem] text-amber-600", children: t('clawSandboxAgentSkills.featuredBadge') })), skill.mentor === mentorUniqueId && (jsx("span", { className: "rounded-full bg-blue-50 px-2 py-0.5 text-[0.625rem] text-blue-600", children: t('clawSandboxAgentSkills.privateBadge') }))] }));
|
|
256974
|
+
// Agent-tab rows are assignments: their only row action is detaching.
|
|
256975
|
+
const renderAssignmentMenu = (assignment) => (jsxs(DropdownMenu, { children: [jsx(DropdownMenuTrigger, { asChild: true, children: jsx(Button$1, { variant: "ghost", size: "sm", "aria-label": t('clawSandboxAgentSkills.skillActions', { name: assignment.skill_name }), children: jsx(Ellipsis, { className: "h-4 w-4" }) }) }), jsx(DropdownMenuContent, { align: "end", children: jsxs(DropdownMenuItem, { className: "cursor-pointer", onClick: () => handleDetach(assignment), children: [jsx(CircleMinus$1, { className: "mr-2 h-4 w-4" }), t('clawSandboxAgentSkills.removeFromAgent')] }) })] }));
|
|
256976
|
+
// Catalog rows carry the full skill object: edit/delete where allowed.
|
|
256977
|
+
const renderSkillMenu = (skill) => {
|
|
256978
|
+
if (!isSkillEditable(skill))
|
|
255292
256979
|
return null;
|
|
255293
|
-
|
|
255294
|
-
|
|
255295
|
-
}
|
|
255296
|
-
|
|
255297
|
-
|
|
255298
|
-
|
|
255299
|
-
|
|
255300
|
-
|
|
255301
|
-
|
|
255302
|
-
|
|
255303
|
-
: 'clawSandboxAgentSkills.skillDisabledLabel', { name: effective.name }) }), renderSkillMenu(effective.skill, effective)] })] }, effective.unique_id));
|
|
255304
|
-
}), jsx(IblPagination, { currentPage: agentPage, totalPages: agentTotalPages, onPageChange: setAgentPage, disabled: isMutatingAgentSkill })] })) }), jsx(TabsContent, { value: "available", className: "space-y-3", children: isLoadingCatalog ? (jsxs("div", { className: "flex items-center gap-2 p-4", role: "status", children: [jsx(LoaderCircle, { className: "h-4 w-4 animate-spin text-blue-500" }), jsx("span", { className: "text-sm text-gray-500", children: t('clawSandboxAgentSkills.loadingSkills') })] })) : catalogTotalCount === 0 ? (jsx("div", { className: "flex items-center justify-center rounded-lg border p-6", children: jsx("p", { className: "text-sm text-gray-500", children: t('clawSandboxAgentSkills.noSkillsAvailable') }) })) : (jsxs(Fragment$1, { children: [catalogSkills.map((skill) => (jsxs("div", { className: "flex items-center justify-between gap-4 rounded-lg border p-4", "data-testid": "available-skill-row", children: [jsxs("div", { className: "min-w-0 space-y-1", children: [jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [jsx("span", { className: "text-sm font-medium text-gray-700", children: skill.name }), jsxs("span", { className: "hidden text-xs text-gray-400 sm:inline", children: ["v", skill.version] }), renderBadges(skill)] }), skill.description && (jsx("p", { className: "line-clamp-2 text-xs text-gray-500", children: skill.description }))] }), jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [isCoveredByAgent(skill) ? (jsx("span", { className: "rounded-full bg-green-50 px-2 py-0.5 text-xs text-green-700", children: t('clawSandboxAgentSkills.addedBadge') })) : (!isOtherAgentsPrivate(skill) && (jsxs(Button$1, { variant: "outline", size: "sm", className: "cursor-pointer", disabled: isMutatingAgentSkill, onClick: () => handleAttach(skill), "aria-label": t('clawSandboxAgentSkills.addToAgentLabel', {
|
|
256980
|
+
return (jsxs(DropdownMenu, { children: [jsx(DropdownMenuTrigger, { asChild: true, children: jsx(Button$1, { variant: "ghost", size: "sm", "aria-label": t('clawSandboxAgentSkills.skillActions', { name: skill.name }), children: jsx(Ellipsis, { className: "h-4 w-4" }) }) }), jsxs(DropdownMenuContent, { align: "end", children: [jsxs(DropdownMenuItem, { className: "cursor-pointer", onClick: () => openEditSkillDialog(skill), children: [jsx(Pen, { className: "mr-2 h-4 w-4" }), t('clawSandboxAgentSkills.edit')] }), jsxs(DropdownMenuItem, { variant: "destructive", className: "cursor-pointer", onClick: () => setDeletingSkill(skill), children: [jsx(Trash2, { className: "mr-2 h-4 w-4" }), t('clawSandboxAgentSkills.delete')] })] })] }));
|
|
256981
|
+
};
|
|
256982
|
+
return (jsxs("div", { className: "space-y-4", "data-testid": "agent-skills-content", children: [jsxs("div", { className: "flex items-start justify-between gap-4", children: [jsxs("p", { className: "flex items-start gap-2 text-xs text-gray-500", children: [jsx(Info$3, { className: "mt-0.5 h-4 w-4 shrink-0 text-gray-400" }), jsx("span", { children: t('clawSandboxAgentSkills.sessionNote') })] }), jsxs(Button$1, { onClick: openNewSkillDialog, size: "sm", className: "shrink-0 cursor-pointer bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: [jsx(Plus, { className: "h-4 w-4" }), t('clawSandboxAgentSkills.newSkill')] })] }), jsxs(Tabs, { defaultValue: "agent", className: "w-full", children: [jsxs(TabsList, { "aria-label": t('clawSandboxAgentSkills.tabsAriaLabel'), "data-testid": "agent-skills-tabs", children: [jsx(TabsTrigger, { value: "agent", "data-testid": "agent-skills-tab-agent", children: t('clawSandboxAgentSkills.tabAgentSkills') }), jsx(TabsTrigger, { value: "available", "data-testid": "agent-skills-tab-available", children: t('clawSandboxAgentSkills.tabAvailableSkills') })] }), jsx(TabsContent, { value: "agent", className: "space-y-3", children: isLoadingAssignments ? (jsxs("div", { className: "flex items-center gap-2 p-4", role: "status", children: [jsx(LoaderCircle, { className: "h-4 w-4 animate-spin text-blue-500" }), jsx("span", { className: "text-sm text-gray-500", children: t('clawSandboxAgentSkills.loadingSkills') })] })) : agentTotalCount === 0 ? (jsx("div", { className: "flex items-center justify-center rounded-lg border p-6", children: jsx("p", { className: "text-sm text-gray-500", children: t('clawSandboxAgentSkills.noEffectiveSkills') }) })) : (jsxs(Fragment$1, { children: [agentAssignments.map((assignment) => (jsxs("div", { className: "flex items-center justify-between gap-4 rounded-lg border p-4", "data-testid": "agent-skill-row", children: [jsx("div", { className: "min-w-0 space-y-1", children: jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [jsx("span", { className: "text-sm font-medium text-gray-700", children: assignment.skill_name }), assignment.skill_slug && (jsxs("span", { className: "hidden text-xs text-gray-400 sm:inline", children: ["/", assignment.skill_slug] }))] }) }), jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [jsx(Switch, { checked: assignment.enabled, onCheckedChange: (checked) => handleToggleAssignment(assignment, checked), disabled: isMutatingAgentSkill, className: "cursor-pointer data-[state=checked]:bg-blue-500", "aria-label": t(assignment.enabled
|
|
256983
|
+
? 'clawSandboxAgentSkills.skillEnabledLabel'
|
|
256984
|
+
: 'clawSandboxAgentSkills.skillDisabledLabel', { name: assignment.skill_name }) }), renderAssignmentMenu(assignment)] })] }, assignment.id))), jsx(IblPagination, { currentPage: agentPage, totalPages: agentTotalPages, onPageChange: setAgentPage, disabled: isMutatingAgentSkill })] })) }), jsx(TabsContent, { value: "available", className: "space-y-3", children: isLoadingCatalog ? (jsxs("div", { className: "flex items-center gap-2 p-4", role: "status", children: [jsx(LoaderCircle, { className: "h-4 w-4 animate-spin text-blue-500" }), jsx("span", { className: "text-sm text-gray-500", children: t('clawSandboxAgentSkills.loadingSkills') })] })) : catalogTotalCount === 0 ? (jsx("div", { className: "flex items-center justify-center rounded-lg border p-6", children: jsx("p", { className: "text-sm text-gray-500", children: t('clawSandboxAgentSkills.noSkillsAvailable') }) })) : (jsxs(Fragment$1, { children: [catalogSkills.map((skill) => (jsxs("div", { className: "flex items-center justify-between gap-4 rounded-lg border p-4", "data-testid": "available-skill-row", children: [jsxs("div", { className: "min-w-0 space-y-1", children: [jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [jsx("span", { className: "text-sm font-medium text-gray-700", children: skill.name }), jsxs("span", { className: "hidden text-xs text-gray-400 sm:inline", children: ["v", skill.version] }), renderBadges(skill)] }), skill.description && (jsx("p", { className: "line-clamp-2 text-xs text-gray-500", children: skill.description }))] }), jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [isCoveredByAgent(skill) ? (jsx("span", { className: "rounded-full bg-green-50 px-2 py-0.5 text-xs text-green-700", children: t('clawSandboxAgentSkills.addedBadge') })) : isOtherAgentsPrivate(skill) ? (
|
|
256985
|
+
// Another agent's private skill: Add stays visible but
|
|
256986
|
+
// disabled, with the reason on hover/focus.
|
|
256987
|
+
jsx(TooltipProvider, { children: jsxs(Tooltip$1, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx("span", { tabIndex: 0, children: jsxs(Button$1, { variant: "outline", size: "sm", disabled: true, "aria-label": t('clawSandboxAgentSkills.addToAgentLabel', {
|
|
256988
|
+
name: skill.name,
|
|
256989
|
+
}), children: [jsx(Plus, { className: "h-4 w-4" }), t('clawSandboxAgentSkills.addToAgent')] }) }) }), jsx(TooltipContent, { className: "ibl-tooltip-content max-w-64", children: jsx("p", { children: t('clawSandboxAgentSkills.privateToOtherAgentTooltip') }) })] }) })) : (jsxs(Button$1, { variant: "outline", size: "sm", className: "cursor-pointer", disabled: isMutatingAgentSkill, onClick: () => handleAttach(skill), "aria-label": t('clawSandboxAgentSkills.addToAgentLabel', {
|
|
255305
256990
|
name: skill.name,
|
|
255306
|
-
}), children: [jsx(Plus, { className: "h-4 w-4" }), t('clawSandboxAgentSkills.addToAgent')] }))
|
|
256991
|
+
}), children: [jsx(Plus, { className: "h-4 w-4" }), t('clawSandboxAgentSkills.addToAgent')] })), isSkillEditable(skill) && (jsx(Switch, { checked: skill.enabled, onCheckedChange: (checked) => handleToggleSkill(skill, checked), disabled: isUpdatingSkill, className: "cursor-pointer data-[state=checked]:bg-blue-500", "aria-label": t(skill.enabled
|
|
256992
|
+
? 'clawSandboxAgentSkills.skillEnabledLabel'
|
|
256993
|
+
: 'clawSandboxAgentSkills.skillDisabledLabel', { name: skill.name }) })), renderSkillMenu(skill)] })] }, skill.id))), jsx(IblPagination, { currentPage: catalogPage, totalPages: catalogTotalPages, onPageChange: setCatalogPage, disabled: isMutatingAgentSkill })] })) })] }), jsx(OverlayModal, { open: showNewDialog, onClose: closeAllDialogs, title: t('clawSandboxAgentSkills.newSkillDialogTitle'), className: "max-w-2xl", children: jsxs("div", { className: "flex h-[min(75dvh,46rem)] flex-col", children: [jsxs("div", { className: "flex-1 space-y-4 overflow-y-auto pb-4 pr-1", children: [jsx(SkillForm, { form: form, onChange: setForm }), jsx("p", { className: "text-xs text-gray-500", children: t('clawSandboxAgentSkills.filesAfterCreateHint') })] }), jsxs("div", { className: "flex justify-end gap-2 border-t pt-4", children: [jsx(Button$1, { variant: "outline", onClick: closeAllDialogs, children: t('clawSandboxAgentSkills.cancel') }), jsx(Button$1, { onClick: handleCreateSkill, disabled: isCreatingSkill || !form.name || !form.slug, className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: isCreatingSkill
|
|
255307
256994
|
? t('clawSandboxAgentSkills.creating')
|
|
255308
|
-
: t('clawSandboxAgentSkills.create') })] })] }) }), jsx(OverlayModal, { open: !!editingSkill, onClose: closeAllDialogs, title: t('clawSandboxAgentSkills.editSkillDialogTitle'), className: "max-w-2xl", children: jsxs(Tabs, { defaultValue: "details", className: "w-full", children: [jsxs(TabsList, { "aria-label": t('clawSandboxAgentSkills.editSkillDialogTitle'), "data-testid": "skill-dialog-tabs", children: [jsx(TabsTrigger, { value: "details", "data-testid": "skill-dialog-tab-details", children: t('clawSandboxAgentSkills.detailsTab') }), jsx(TabsTrigger, { value: "files", "data-testid": "skill-dialog-tab-files", children: t('clawSandboxAgentSkills.filesTab') })] }), jsx(TabsContent, { value: "details", children: jsxs("div", { className: "flex h-[min(75dvh,46rem)] flex-col", children: [jsx("div", { className: "flex-1 space-y-4 overflow-y-auto pb-4 pr-1", children: jsx(SkillForm, { form: form, onChange: setForm }) }), jsxs("div", { className: "flex justify-end gap-2 border-t pt-4", children: [jsx(Button$1, { variant: "outline", onClick: closeAllDialogs, children: t('clawSandboxAgentSkills.cancel') }), jsx(Button$1, { onClick: handleUpdateSkill, disabled: isUpdatingSkill || !form.name || !form.slug, className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: isUpdatingSkill
|
|
256995
|
+
: t('clawSandboxAgentSkills.create') })] })] }) }), jsx(OverlayModal, { open: !!editingSkill, onClose: closeAllDialogs, title: t('clawSandboxAgentSkills.editSkillDialogTitle'), className: "max-w-2xl", children: jsxs(Tabs, { defaultValue: "details", className: "w-full", children: [jsxs(TabsList, { "aria-label": t('clawSandboxAgentSkills.editSkillDialogTitle'), "data-testid": "skill-dialog-tabs", children: [jsx(TabsTrigger, { value: "details", "data-testid": "skill-dialog-tab-details", children: t('clawSandboxAgentSkills.detailsTab') }), jsx(TabsTrigger, { value: "files", "data-testid": "skill-dialog-tab-files", children: t('clawSandboxAgentSkills.filesTab') })] }), jsx(TabsContent, { value: "details", children: jsxs("div", { className: "flex h-[min(75dvh,46rem)] flex-col", children: [jsx("div", { className: "flex-1 space-y-4 overflow-y-auto pb-4 pr-1", children: jsx(SkillForm, { form: form, onChange: setForm, privateLocked: !!editingSkill && isOtherAgentsPrivate(editingSkill) }) }), jsxs("div", { className: "flex justify-end gap-2 border-t pt-4", children: [jsx(Button$1, { variant: "outline", onClick: closeAllDialogs, children: t('clawSandboxAgentSkills.cancel') }), jsx(Button$1, { onClick: handleUpdateSkill, disabled: isUpdatingSkill || !form.name || !form.slug, className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: isUpdatingSkill
|
|
255309
256996
|
? t('clawSandboxAgentSkills.saving')
|
|
255310
|
-
: t('clawSandboxAgentSkills.save') })] })] }) }), jsx(TabsContent, { value: "files", children: jsx("div", { className: "flex h-[min(75dvh,46rem)] flex-col", children: jsx("div", { className: "flex-1 space-y-4 overflow-y-auto pb-4 pr-1", children: editingSkill && (jsx(SkillResourcesSection, { platformKey: platformKey, skillId: editingSkill.id })) }) }) })] }) }), jsxs(OverlayModal, { open: !!deletingSkill, onClose: () => setDeletingSkill(null), title: t('clawSandboxAgentSkills.deleteSkillDialogTitle'), className: "max-w-md", children: [jsx("p", { className: "mt-2 text-sm text-gray-600", children: t('clawSandboxAgentSkills.deleteConfirmation', { name: (
|
|
256997
|
+
: t('clawSandboxAgentSkills.save') })] })] }) }), jsx(TabsContent, { value: "files", children: jsx("div", { className: "flex h-[min(75dvh,46rem)] flex-col", children: jsx("div", { className: "flex-1 space-y-4 overflow-y-auto pb-4 pr-1", children: editingSkill && (jsx(SkillResourcesSection, { platformKey: platformKey, skillId: editingSkill.id })) }) }) })] }) }), jsxs(OverlayModal, { open: !!deletingSkill, onClose: () => setDeletingSkill(null), title: t('clawSandboxAgentSkills.deleteSkillDialogTitle'), className: "max-w-md", children: [jsx("p", { className: "mt-2 text-sm text-gray-600", children: t('clawSandboxAgentSkills.deleteConfirmation', { name: (_f = deletingSkill === null || deletingSkill === void 0 ? void 0 : deletingSkill.name) !== null && _f !== void 0 ? _f : '' }) }), jsxs("div", { className: "flex justify-end gap-2 pt-2", children: [jsx(Button$1, { variant: "outline", onClick: () => setDeletingSkill(null), children: t('clawSandboxAgentSkills.cancel') }), jsx(Button$1, { onClick: handleDeleteSkill, disabled: isDeletingSkill, className: "bg-red-600 text-white hover:bg-red-700", children: isDeletingSkill
|
|
255311
256998
|
? t('clawSandboxAgentSkills.deleting')
|
|
255312
256999
|
: t('clawSandboxAgentSkills.delete') })] })] })] }));
|
|
255313
257000
|
}
|
|
255314
257001
|
// ── Shared Skill Form ────────────────────────────────────────────────────────
|
|
255315
|
-
function SkillForm({ form, onChange, }) {
|
|
257002
|
+
function SkillForm({ form, onChange, privateLocked = false, }) {
|
|
255316
257003
|
const t = useT();
|
|
255317
|
-
return (jsxs("div", { className: "mt-4 grid gap-4", children: [jsxs("div", { className: "flex items-center justify-between rounded-lg border p-4", children: [jsxs("div", { className: "space-y-1", children: [jsx(Label$1, { htmlFor: "skill-private", className: "text-sm font-medium text-[#646464]", children: t('clawSandboxAgentSkills.privateToAgent') }), jsx("p", { className: "text-xs text-gray-500", children: t(
|
|
257004
|
+
return (jsxs("div", { className: "mt-4 grid gap-4", children: [jsxs("div", { className: "flex items-center justify-between rounded-lg border p-4", children: [jsxs("div", { className: "space-y-1", children: [jsx(Label$1, { htmlFor: "skill-private", className: "text-sm font-medium text-[#646464]", children: t('clawSandboxAgentSkills.privateToAgent') }), jsx("p", { className: "text-xs text-gray-500", children: t(privateLocked
|
|
257005
|
+
? 'clawSandboxAgentSkills.privateToOtherAgentHint'
|
|
257006
|
+
: 'clawSandboxAgentSkills.privateToAgentHint') })] }), jsx(Switch, { id: "skill-private", checked: form.privateToAgent, onCheckedChange: (checked) => onChange({ ...form, privateToAgent: checked }), disabled: privateLocked, className: "cursor-pointer data-[state=checked]:bg-blue-500" })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "skill-name", className: "text-sm font-medium text-[#646464]", children: t('clawSandboxAgentSkills.fieldName') }), jsx(Input, { id: "skill-name", name: "skill-name", value: form.name, onChange: (e) => onChange({ ...form, name: e.target.value }), placeholder: t('clawSandboxAgentSkills.fieldNamePlaceholder'), className: "py-6" })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "skill-slug", className: "text-sm font-medium text-[#646464]", children: t('clawSandboxAgentSkills.fieldSlug') }), jsx(Input, { id: "skill-slug", name: "skill-slug", value: form.slug, onChange: (e) => onChange({ ...form, slug: e.target.value }), placeholder: t('clawSandboxAgentSkills.fieldSlugPlaceholder'), className: "py-6" })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "skill-version", className: "text-sm font-medium text-[#646464]", children: t('clawSandboxAgentSkills.fieldVersion') }), jsx(Input, { id: "skill-version", name: "skill-version", value: form.version, onChange: (e) => onChange({ ...form, version: e.target.value }), placeholder: t('clawSandboxAgentSkills.fieldVersionPlaceholder'), className: "py-6" })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "skill-category", className: "text-sm font-medium text-[#646464]", children: t('clawSandboxAgentSkills.fieldCategory') }), jsx(Input, { id: "skill-category", name: "skill-category", value: form.category, onChange: (e) => onChange({ ...form, category: e.target.value }), placeholder: t('clawSandboxAgentSkills.fieldCategoryPlaceholder'), className: "py-6" })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { htmlFor: "skill-description", className: "text-sm font-medium text-[#646464]", children: t('clawSandboxAgentSkills.fieldDescription') }), jsx(Textarea, { id: "skill-description", name: "skill-description", value: form.description, onChange: (e) => onChange({ ...form, description: e.target.value }), placeholder: t('clawSandboxAgentSkills.fieldDescriptionPlaceholder'), rows: 3 })] }), jsxs("div", { className: "space-y-2 overflow-hidden", children: [jsx(Label$1, { htmlFor: "skill-instruction", className: "text-sm font-medium text-[#646464]", children: t('clawSandboxAgentSkills.fieldInstruction') }), jsx("p", { className: "text-xs text-gray-500", children: t('clawSandboxAgentSkills.instructionHint') }), jsx(RichTextEditor, { value: form.instruction, onChange: (value) => onChange({ ...form, instruction: value }) })] })] }));
|
|
255318
257007
|
}
|
|
255319
257008
|
|
|
255320
257009
|
/** Distance (px) from the list bottom at which the next page is requested. */
|
|
@@ -255585,46 +257274,6 @@ function ProjectMentorsList({ projectMentors, onMentorRemoved, onAddMentorClick,
|
|
|
255585
257274
|
}) }), showMinimumMentorAlert && (jsx(Suspense, { fallback: null, children: jsx(MinimumMentorAlert$1, { open: showMinimumMentorAlert, onOpenChange: setShowMinimumMentorAlert }) }))] }));
|
|
255586
257275
|
}
|
|
255587
257276
|
|
|
255588
|
-
const AgentSettingsContext = React__default.createContext(null);
|
|
255589
|
-
function AgentSettingsProvider({ children, ...value }) {
|
|
255590
|
-
const ctx = React__default.useMemo(() => value,
|
|
255591
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
255592
|
-
[
|
|
255593
|
-
value.tenantKey,
|
|
255594
|
-
value.mentorId,
|
|
255595
|
-
value.username,
|
|
255596
|
-
value.enableRBAC,
|
|
255597
|
-
value.rbacPermissions,
|
|
255598
|
-
value.executeGatedAction,
|
|
255599
|
-
value.visibilityOptions,
|
|
255600
|
-
]);
|
|
255601
|
-
return jsx(AgentSettingsContext.Provider, { value: ctx, children: children });
|
|
255602
|
-
}
|
|
255603
|
-
/**
|
|
255604
|
-
* Read the shared agent identity / config from the nearest
|
|
255605
|
-
* `<AgentSettingsProvider>` if one is present, returning `null` otherwise.
|
|
255606
|
-
*
|
|
255607
|
-
* Use this when a tab can also be driven by direct props (i.e. the
|
|
255608
|
-
* `<AgentSettingsProvider>` wrapper is optional). For tabs that strictly
|
|
255609
|
-
* require the provider, use `useAgentSettings`.
|
|
255610
|
-
*/
|
|
255611
|
-
function useAgentSettingsOptional() {
|
|
255612
|
-
return React__default.useContext(AgentSettingsContext);
|
|
255613
|
-
}
|
|
255614
|
-
/**
|
|
255615
|
-
* Read the shared agent identity / config from the nearest
|
|
255616
|
-
* `<AgentSettingsProvider>`. Throws if called outside a provider — this is
|
|
255617
|
-
* intentional: every tab MUST be wrapped.
|
|
255618
|
-
*/
|
|
255619
|
-
function useAgentSettings() {
|
|
255620
|
-
const ctx = React__default.useContext(AgentSettingsContext);
|
|
255621
|
-
if (!ctx) {
|
|
255622
|
-
throw new Error('useAgentSettings must be used within an <AgentSettingsProvider>. ' +
|
|
255623
|
-
'Wrap your tab components with <AgentSettingsProvider tenantKey=... mentorId=... username=... enableRBAC=...>.');
|
|
255624
|
-
}
|
|
255625
|
-
return ctx;
|
|
255626
|
-
}
|
|
255627
|
-
|
|
255628
257277
|
function useDatasetsWithPagination(itemsPerPage = 5, opts = {}) {
|
|
255629
257278
|
const { page: controlledPage, search: controlledSearch, onPageChange, onSearchChange } = opts;
|
|
255630
257279
|
// Controlled/uncontrolled is decided per axis, independently.
|
|
@@ -258163,9 +259812,6 @@ var addMentorToProjectModal = /*#__PURE__*/Object.freeze({
|
|
|
258163
259812
|
AddMentorToProjectModal: AddMentorToProjectModal
|
|
258164
259813
|
});
|
|
258165
259814
|
|
|
258166
|
-
const CustomSwitch = React.forwardRef(({ className, checked, onCheckedChange, ...props }, ref) => (jsx(Root$c, { checked: checked, onCheckedChange: onCheckedChange, className: cn('peer focus-visible:ring-ring focus-visible:ring-offset-background inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-blue-500 data-[state=unchecked]:bg-gray-200', className), ...props, ref: ref, children: jsx(Thumb, { className: cn('pointer-events-none block h-5 w-5 rounded-full bg-white shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0') }) })));
|
|
258167
|
-
CustomSwitch.displayName = Root$c.displayName;
|
|
258168
|
-
|
|
258169
259815
|
const DeleteDatasetModal$1 = dynamic(() => Promise.resolve().then(function () { return deleteDatasetModal; }).then((mod) => ({
|
|
258170
259816
|
default: mod.DeleteDatasetModal,
|
|
258171
259817
|
})));
|
|
@@ -258290,36 +259936,6 @@ function DatasetItemList({ datasets, onSelect, selectedDatasetId, labels }) {
|
|
|
258290
259936
|
return (jsx(TableBody, { children: (datasets === null || datasets === void 0 ? void 0 : datasets.length) > 0 ? (datasets === null || datasets === void 0 ? void 0 : datasets.map((dataset) => (jsx(DatasetItem, { dataset: dataset, onSelect: onSelect, isSelected: selectedDatasetId === dataset.id, labels: labels }, dataset.id)))) : (jsx(TableRow$1, { children: jsx(TableCell$1, { colSpan: 6, className: "p-4 text-center text-[#646464]", children: (_b = (_a = labels === null || labels === void 0 ? void 0 : labels.table) === null || _a === void 0 ? void 0 : _a.emptyState) !== null && _b !== void 0 ? _b : 'No items found' }) })) }));
|
|
258291
259937
|
}
|
|
258292
259938
|
|
|
258293
|
-
/**
|
|
258294
|
-
* Shared utilities for tab label systems.
|
|
258295
|
-
*
|
|
258296
|
-
* Every tab exports a `resolve*Labels(override?)` function that deep-merges
|
|
258297
|
-
* a partial override into the default AGENT labels. This file provides the
|
|
258298
|
-
* DeepPartial type and deepMerge implementation they all share.
|
|
258299
|
-
*/
|
|
258300
|
-
function deepMerge(base, override) {
|
|
258301
|
-
const result = { ...base };
|
|
258302
|
-
const overrideBag = override;
|
|
258303
|
-
for (const key of Object.keys(overrideBag)) {
|
|
258304
|
-
const overrideValue = overrideBag[key];
|
|
258305
|
-
if (overrideValue === undefined)
|
|
258306
|
-
continue;
|
|
258307
|
-
const baseValue = base[key];
|
|
258308
|
-
if (baseValue &&
|
|
258309
|
-
typeof baseValue === 'object' &&
|
|
258310
|
-
!Array.isArray(baseValue) &&
|
|
258311
|
-
overrideValue &&
|
|
258312
|
-
typeof overrideValue === 'object' &&
|
|
258313
|
-
!Array.isArray(overrideValue)) {
|
|
258314
|
-
result[key] = deepMerge(baseValue, overrideValue);
|
|
258315
|
-
}
|
|
258316
|
-
else {
|
|
258317
|
-
result[key] = overrideValue;
|
|
258318
|
-
}
|
|
258319
|
-
}
|
|
258320
|
-
return result;
|
|
258321
|
-
}
|
|
258322
|
-
|
|
258323
259939
|
const AGENT_DATASETS_TAB_LABELS = {
|
|
258324
259940
|
header: {
|
|
258325
259941
|
title: 'Datasets',
|
|
@@ -258612,10 +260228,21 @@ const resourceTypes = [
|
|
|
258612
260228
|
},
|
|
258613
260229
|
];
|
|
258614
260230
|
|
|
260231
|
+
/**
|
|
260232
|
+
* Spend-cap blocks (HTTP 429) carry the machine code in `error`/`error_code`
|
|
260233
|
+
* and a friendly, learner-safe sentence in `message`. Detection keys on
|
|
260234
|
+
* `error_code` — never on the HTTP status, which can also mean a provider
|
|
260235
|
+
* rate limit.
|
|
260236
|
+
*/
|
|
260237
|
+
const SPEND_CAP_EXCEEDED_ERROR_CODE = 'spend_cap_exceeded';
|
|
258615
260238
|
const extractErrorMessage$1 = (error, fallback) => {
|
|
258616
|
-
var _a, _b;
|
|
260239
|
+
var _a, _b, _c;
|
|
258617
260240
|
const apiError = error;
|
|
258618
|
-
|
|
260241
|
+
if (((_a = apiError === null || apiError === void 0 ? void 0 : apiError.data) === null || _a === void 0 ? void 0 : _a.error_code) === SPEND_CAP_EXCEEDED_ERROR_CODE) {
|
|
260242
|
+
// Surface the friendly message, never the raw `spend_cap_exceeded` code.
|
|
260243
|
+
return apiError.data.message || fallback;
|
|
260244
|
+
}
|
|
260245
|
+
return ((_b = apiError === null || apiError === void 0 ? void 0 : apiError.data) === null || _b === void 0 ? void 0 : _b.error) || ((_c = apiError === null || apiError === void 0 ? void 0 : apiError.error) === null || _c === void 0 ? void 0 : _c.error) || fallback;
|
|
258619
260246
|
};
|
|
258620
260247
|
|
|
258621
260248
|
function useGithubRepoDetails(url, tenantKey) {
|
|
@@ -260396,10 +262023,10 @@ function Account({ tenant, tenants = [], username, onInviteClick, email, mainPla
|
|
|
260396
262023
|
onTabChange === null || onTabChange === void 0 ? void 0 : onTabChange('billing');
|
|
260397
262024
|
}, className: `flex items-center gap-2 px-3 py-2 rounded-lg whitespace-nowrap text-sm transition-all ${activeTab === 'billing'
|
|
260398
262025
|
? 'bg-blue-50 text-blue-600 font-medium'
|
|
260399
|
-
: 'text-gray-600 hover:bg-gray-50'}`, children: [jsx(CreditCard, { className: "h-4 w-4" }), jsx("span", { className: "hidden sm:inline", children: t('profileAccount.tabBilling') }), jsx("span", { className: "sm:hidden", children: t('profileAccount.tabBilling') })] }, 'billing'))] }) }) }), jsxs("div", { className: "flex-1 flex flex-col overflow-hidden", style: { height: '100%' }, children: [jsxs("div", { className: "hidden lg:block flex-shrink-0 p-6 border-b border-gray-200 bg-white dark:bg-gray-900", children: [jsx("h3", { className: "text-lg font-medium text-gray-900 dark:text-gray-100 mb-2", children: (_b = (_a = TABS.find((t) => t.id === activeTab)) === null || _a === void 0 ? void 0 : _a.label) !== null && _b !== void 0 ? _b : activeTab }), jsxs("p", { className: "text-gray-600 dark:text-gray-400 text-sm", children: [activeTab === 'management' && t('profileAccount.descManagement'), activeTab === 'organization' && t('profileAccount.descOrganization'), activeTab === 'integrations' && t('profileAccount.descIntegrations'), activeTab === 'billing' && t('profileAccount.descBilling'), activeTab === 'monetization' && t('profileAccount.descMonetization'), activeTab === 'datasets' && t('profileAccount.descBenchmarks'), activeTab === 'advanced' && t('profileAccount.descAdvanced')] })] }), jsxs("div", { className: "flex-1 p-6 space-y-6", style: {
|
|
262026
|
+
: 'text-gray-600 hover:bg-gray-50'}`, children: [jsx(CreditCard, { className: "h-4 w-4" }), jsx("span", { className: "hidden sm:inline", children: t('profileAccount.tabBilling') }), jsx("span", { className: "sm:hidden", children: t('profileAccount.tabBilling') })] }, 'billing'))] }) }) }), jsxs("div", { className: "flex-1 flex flex-col overflow-hidden", style: { height: '100%' }, children: [jsxs("div", { className: "hidden lg:block flex-shrink-0 p-6 border-b border-gray-200 bg-white dark:bg-gray-900", children: [jsx("h3", { className: "text-lg font-medium text-gray-900 dark:text-gray-100 mb-2", children: (_b = (_a = TABS.find((t) => t.id === activeTab)) === null || _a === void 0 ? void 0 : _a.label) !== null && _b !== void 0 ? _b : (activeTab === 'billing' ? t('profileAccount.tabBilling') : activeTab) }), jsxs("p", { className: "text-gray-600 dark:text-gray-400 text-sm", children: [activeTab === 'management' && t('profileAccount.descManagement'), activeTab === 'organization' && t('profileAccount.descOrganization'), activeTab === 'integrations' && t('profileAccount.descIntegrations'), activeTab === 'billing' && t('profileAccount.descBilling'), activeTab === 'monetization' && t('profileAccount.descMonetization'), activeTab === 'datasets' && t('profileAccount.descBenchmarks'), activeTab === 'advanced' && t('profileAccount.descAdvanced')] })] }), jsxs("div", { className: "flex-1 p-6 space-y-6", style: {
|
|
260400
262027
|
overflowY: 'auto',
|
|
260401
262028
|
overflowX: 'hidden',
|
|
260402
|
-
}, children: [jsxs("div", { className: "lg:hidden mb-6", children: [jsx("h3", { className: "text-lg font-medium text-gray-900 dark:text-gray-100 mb-2", children: (_d = (_c = TABS.find((t) => t.id === activeTab)) === null || _c === void 0 ? void 0 : _c.label) !== null && _d !== void 0 ? _d : activeTab }), jsxs("p", { className: "text-gray-600 dark:text-gray-400 text-sm", children: [activeTab === 'basic' && t('profileAccount.descBasic'), activeTab === 'social' && t('profileAccount.descSocial'), activeTab === 'security' && t('profileAccount.descSecurity'), activeTab === 'management' && t('profileAccount.descManagement'), activeTab === 'organization' && t('profileAccount.descOrganization'), activeTab === 'monetization' && t('profileAccount.descMonetization'), activeTab === 'datasets' && t('profileAccount.descBenchmarks'), activeTab === 'advanced' && t('profileAccount.descAdvanced')] })] }), activeTab === 'management' && hasManagementPermissions && (jsx(Admin, { authSpaUrl: authURL, onInviteClick: onInviteClick, tenant: tenant, currentSpa: currentSPA, hasUserTabPermission: hasUserTabPermission, hasGroupsTabPermission: hasGroupsTabPermission, hasPoliciesTabPermission: hasPoliciesTabPermission, hasRolesTabPermission: hasRolesTabPermission, hasTeamsTabPermission: hasTeamsTabPermission, hasInviteUserPermission: hasInviteUserPermission, hasCreateTeamPermission: hasCreateTeamPermission, onLoadGroupPermissions: onLoadGroupPermissions, hasAlertsTabPermission: hasAlertsTabPermission, rbacPermissions: rbacPermissions, enableRbac: enableRbac, showGradebookTab: showGradebookTab })), activeTab === 'organization' && (jsx(OrganizationTab, { platformKey: tenant, tenant: tenants.find((ten) => ten.key === tenant), onTenantUpdate: onTenantUpdate, setOrganizationLogoFromOutside: setOrganizationLogo, defaultSupportPhone: defaultSupportPhone, enableSupportPhone: enableSupportPhone })), activeTab === 'integrations' && (jsx(IntegrationsTab, { tenantKey: tenant, username: username })), activeTab === 'billing' && (jsx(BillingTab, { tenant: tenant, userActiveApp: userActiveApp, username: username, currentUserEmail: email, mainPlatformKey: mainPlatformKey })), activeTab === 'monetization' && (jsx(MonetizationTab, { platformKey: tenant, authURL: authURL })), activeTab === 'datasets' && showBenchmarks && (jsx(BenchmarksTab, { tenantKey: tenant, username: username })), activeTab === 'advanced' && (jsx(AdvancedTab, { platformKey: tenant, currentSPA: currentSPA, username: username, authURL: authURL, currentPlatformBaseDomain: currentPlatformBaseDomain }))] })] }), jsx(ToastProvider, {})] }));
|
|
262029
|
+
}, children: [jsxs("div", { className: "lg:hidden mb-6", children: [jsx("h3", { className: "text-lg font-medium text-gray-900 dark:text-gray-100 mb-2", children: (_d = (_c = TABS.find((t) => t.id === activeTab)) === null || _c === void 0 ? void 0 : _c.label) !== null && _d !== void 0 ? _d : (activeTab === 'billing' ? t('profileAccount.tabBilling') : activeTab) }), jsxs("p", { className: "text-gray-600 dark:text-gray-400 text-sm", children: [activeTab === 'billing' && t('profileAccount.descBilling'), activeTab === 'basic' && t('profileAccount.descBasic'), activeTab === 'social' && t('profileAccount.descSocial'), activeTab === 'security' && t('profileAccount.descSecurity'), activeTab === 'management' && t('profileAccount.descManagement'), activeTab === 'organization' && t('profileAccount.descOrganization'), activeTab === 'monetization' && t('profileAccount.descMonetization'), activeTab === 'datasets' && t('profileAccount.descBenchmarks'), activeTab === 'advanced' && t('profileAccount.descAdvanced')] })] }), activeTab === 'management' && hasManagementPermissions && (jsx(Admin, { authSpaUrl: authURL, onInviteClick: onInviteClick, tenant: tenant, currentSpa: currentSPA, hasUserTabPermission: hasUserTabPermission, hasGroupsTabPermission: hasGroupsTabPermission, hasPoliciesTabPermission: hasPoliciesTabPermission, hasRolesTabPermission: hasRolesTabPermission, hasTeamsTabPermission: hasTeamsTabPermission, hasInviteUserPermission: hasInviteUserPermission, hasCreateTeamPermission: hasCreateTeamPermission, onLoadGroupPermissions: onLoadGroupPermissions, hasAlertsTabPermission: hasAlertsTabPermission, rbacPermissions: rbacPermissions, enableRbac: enableRbac, showGradebookTab: showGradebookTab })), activeTab === 'organization' && (jsx(OrganizationTab, { platformKey: tenant, tenant: tenants.find((ten) => ten.key === tenant), onTenantUpdate: onTenantUpdate, setOrganizationLogoFromOutside: setOrganizationLogo, defaultSupportPhone: defaultSupportPhone, enableSupportPhone: enableSupportPhone })), activeTab === 'integrations' && (jsx(IntegrationsTab, { tenantKey: tenant, username: username })), activeTab === 'billing' && (jsx(BillingTab, { tenant: tenant, userActiveApp: userActiveApp, username: username, currentUserEmail: email, mainPlatformKey: mainPlatformKey })), activeTab === 'monetization' && (jsx(MonetizationTab, { platformKey: tenant, authURL: authURL })), activeTab === 'datasets' && showBenchmarks && (jsx(BenchmarksTab, { tenantKey: tenant, username: username })), activeTab === 'advanced' && (jsx(AdvancedTab, { platformKey: tenant, currentSPA: currentSPA, username: username, authURL: authURL, currentPlatformBaseDomain: currentPlatformBaseDomain }))] })] }), jsx(ToastProvider, {})] }));
|
|
260403
262030
|
}
|
|
260404
262031
|
|
|
260405
262032
|
function UserProfileModal({ isOpen, onClose, params, email, mainPlatformKey, showMentorAIDisplayCheckbox = false, showLeaderboardDisplayCheckbox = false, showGradebookTab = false, showUsernameField = false, showPlatformName = false, useGravatarPicFallback = true, enableCatalogInvite = false, targetTab = 'basic', currentPlan = '', currentSPA = '', userActiveApp = null, authURL, tenants = [], onTenantUpdate, currentPlatformBaseDomain = '', rbacPermissions = {}, enableRbac = false, onLoadGroupPermissions, onTabChange, defaultSupportPhone = '', onBillingTabRequest, onAccountDeleted, enableMemoryTab = false, showBenchmarks = false, localLLMProps, enableSupportPhone, }) {
|
|
@@ -268957,20 +270584,6 @@ function AgentSafetyTab({ renderPromptContent, FlaggedPromptsModal, showFlaggedP
|
|
|
268957
270584
|
selectedPrompt: selectedPrompt, isEditing: isEditMentorLoading })), isFlaggedPromptsModalOpen && FlaggedPromptsModal && (jsx(FlaggedPromptsModal, { isOpen: isFlaggedPromptsModalOpen, onClose: () => setIsFlaggedPromptsModalOpen(false), mentorId: mentorId, tenantKey: tenantKey, username: username !== null && username !== void 0 ? username : '' }))] }) })] }));
|
|
268958
270585
|
}
|
|
268959
270586
|
|
|
268960
|
-
/**
|
|
268961
|
-
* Reusable header used at the top of every capability-gated agent-settings
|
|
268962
|
-
* tab. It surfaces the capability's on/off toggle (previously buried in the
|
|
268963
|
-
* Settings → Capabilities list) right where the feature is configured, with a
|
|
268964
|
-
* naive-friendly explanation.
|
|
268965
|
-
*
|
|
268966
|
-
* The configuration UI below is always rendered so admins can preview what
|
|
268967
|
-
* they'll get, but it's grayed out and made inert (`pointer-events-none`,
|
|
268968
|
-
* `aria-hidden`) until the capability is switched on.
|
|
268969
|
-
*/
|
|
268970
|
-
function CapabilityGate({ title, description, enabled, onToggle, disabled = false, ariaLabel, offHint, toggleTestId, children, }) {
|
|
268971
|
-
return (jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-start justify-between gap-4 rounded-lg border border-gray-200 bg-gray-50/60 p-4", "data-testid": "capability-gate-header", children: [jsx("div", { className: "min-w-0", children: jsxs("p", { className: "text-xs leading-relaxed text-gray-600", children: [description, !enabled && offHint && (jsxs("span", { className: "font-medium text-gray-500", "data-testid": "capability-gate-off-hint", children: [' ', offHint] }))] }) }), jsx(CustomSwitch, { checked: enabled, onCheckedChange: onToggle, disabled: disabled, "aria-label": ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : title, "aria-checked": enabled, "data-testid": toggleTestId, className: "mt-0.5 flex-shrink-0" })] }), jsx("div", { className: cn('transition-opacity', !enabled && 'pointer-events-none select-none opacity-50'), "aria-hidden": !enabled, inert: !enabled, "data-testid": "capability-gate-content", "data-enabled": enabled, children: children })] }));
|
|
268972
|
-
}
|
|
268973
|
-
|
|
268974
270587
|
const AGENT_PRIVACY_TAB_LABELS = {
|
|
268975
270588
|
header: {
|
|
268976
270589
|
title: 'Privacy',
|
|
@@ -271213,6 +272826,46 @@ function AgentGraderTab({ labels: labelsOverride, tenantKey: tenantKeyProp, ment
|
|
|
271213
272826
|
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 }) })] })] })) })] }));
|
|
271214
272827
|
}
|
|
271215
272828
|
|
|
272829
|
+
/**
|
|
272830
|
+
* Learner-safe spend-cap usage indicator. The status endpoint intentionally
|
|
272831
|
+
* returns only zones and fill percentages — never dollar amounts — so this
|
|
272832
|
+
* renders progress toward each cap, a warning banner near the limit, and a
|
|
272833
|
+
* blocked banner once a hard-block cap is exceeded. Admin dollar figures live
|
|
272834
|
+
* in the `AgentSpendCapsTab` settings tab instead.
|
|
272835
|
+
*/
|
|
272836
|
+
function SpendCapUsage({ tenantKey, userId, mentorId, showWhenOk = false, className, }) {
|
|
272837
|
+
const t = useT();
|
|
272838
|
+
const { data, isLoading, error } = useGetSpendCapStatusQuery({ org: tenantKey, userId, mentor: mentorId }, { skip: !tenantKey || !userId });
|
|
272839
|
+
if (isLoading || error || !data)
|
|
272840
|
+
return null;
|
|
272841
|
+
const summary = data;
|
|
272842
|
+
if (summary.caps.length === 0)
|
|
272843
|
+
return null;
|
|
272844
|
+
if (summary.status === 'ok' && !showWhenOk)
|
|
272845
|
+
return null;
|
|
272846
|
+
const scopeLabels = {
|
|
272847
|
+
tenant: t('spendCapUsage.scopeTenant'),
|
|
272848
|
+
agent: t('spendCapUsage.scopeAgent'),
|
|
272849
|
+
user_agent: t('spendCapUsage.scopeUserAgent'),
|
|
272850
|
+
};
|
|
272851
|
+
const intervalLabels = {
|
|
272852
|
+
day: t('spendCapUsage.intervalDay'),
|
|
272853
|
+
week: t('spendCapUsage.intervalWeek'),
|
|
272854
|
+
month: t('spendCapUsage.intervalMonth'),
|
|
272855
|
+
year: t('spendCapUsage.intervalYear'),
|
|
272856
|
+
};
|
|
272857
|
+
return (jsxs("div", { className: cn('space-y-3 rounded-lg border border-gray-200 bg-white p-3', className), "data-testid": "spend-cap-usage", children: [summary.blocked ? (jsxs("div", { className: "flex items-start gap-2 rounded-lg border border-red-200 bg-red-50 p-3", "data-testid": "spend-cap-usage-blocked", children: [jsx(OctagonX, { className: "mt-0.5 h-4 w-4 flex-shrink-0 text-red-600" }), jsx("p", { className: "text-xs leading-relaxed text-red-800", children: t('spendCapUsage.blockedBanner') })] })) : summary.status !== 'ok' ? (jsxs("div", { className: "flex items-start gap-2 rounded-lg border border-amber-200 bg-amber-50 p-3", "data-testid": "spend-cap-usage-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: t('spendCapUsage.warningBanner') })] })) : null, jsx("ul", { className: "space-y-2", children: summary.caps.map((cap, index) => {
|
|
272858
|
+
var _a, _b;
|
|
272859
|
+
const percent = Math.min(100, Math.max(0, Math.round(cap.percent_used)));
|
|
272860
|
+
const barColor = cap.status === 'exceeded'
|
|
272861
|
+
? 'bg-red-500'
|
|
272862
|
+
: cap.status === 'warning'
|
|
272863
|
+
? 'bg-amber-500'
|
|
272864
|
+
: 'bg-blue-500';
|
|
272865
|
+
return (jsxs("li", { className: "space-y-1", "data-testid": `spend-cap-usage-item-${cap.scope}`, children: [jsxs("div", { className: "flex items-center justify-between text-xs text-gray-600", children: [jsxs("span", { children: [scopeLabels[cap.scope], " \u00B7 ", intervalLabels[cap.interval_type]] }), jsx("span", { "data-testid": `spend-cap-usage-percent-${cap.scope}`, children: t('spendCapUsage.percentUsed', { percent }) })] }), jsx("div", { className: "h-1.5 w-full overflow-hidden rounded-full bg-gray-200", role: "progressbar", "aria-valuenow": percent, "aria-valuemin": 0, "aria-valuemax": 100, "aria-label": `${scopeLabels[cap.scope]} · ${intervalLabels[cap.interval_type]}`, children: jsx("div", { className: cn('h-full rounded-full transition-all', barColor), style: { width: `${percent}%` } }) })] }, `${cap.scope}-${(_a = cap.mentor_unique_id) !== null && _a !== void 0 ? _a : ''}-${(_b = cap.username) !== null && _b !== void 0 ? _b : ''}-${index}`));
|
|
272866
|
+
}) })] }));
|
|
272867
|
+
}
|
|
272868
|
+
|
|
271216
272869
|
function ApiKeyModal({ isOpen, onClose, apiKey, labels }) {
|
|
271217
272870
|
const { copy, status } = useCopyToClipboard(800);
|
|
271218
272871
|
const Icon = status === 'success' ? Check : Copy;
|
|
@@ -280843,6 +282496,27 @@ function Chat({ mode = 'default', isPreviewMode = false, hasBorder = true, isInC
|
|
|
280843
282496
|
closeButton: true,
|
|
280844
282497
|
});
|
|
280845
282498
|
}, []);
|
|
282499
|
+
// Spend-cap block (429, error_code "spend_cap_exceeded") — deliberately
|
|
282500
|
+
// separate from the 402 credits flow. Learner-safe: names only the interval,
|
|
282501
|
+
// never dollar amounts.
|
|
282502
|
+
const handleSpendCapError = useCallback((data) => {
|
|
282503
|
+
var _a;
|
|
282504
|
+
const interval = (_a = data === null || data === void 0 ? void 0 : data.details) === null || _a === void 0 ? void 0 : _a.interval_type;
|
|
282505
|
+
const message = interval === 'day'
|
|
282506
|
+
? t('chatIndex.spendCapLimitDay')
|
|
282507
|
+
: interval === 'week'
|
|
282508
|
+
? t('chatIndex.spendCapLimitWeek')
|
|
282509
|
+
: interval === 'month'
|
|
282510
|
+
? t('chatIndex.spendCapLimitMonth')
|
|
282511
|
+
: interval === 'year'
|
|
282512
|
+
? t('chatIndex.spendCapLimitYear')
|
|
282513
|
+
: t('chatIndex.spendCapLimitGeneric');
|
|
282514
|
+
toast.error(message, {
|
|
282515
|
+
description: t('chatIndex.spendCapContactAdmin'),
|
|
282516
|
+
closeButton: true,
|
|
282517
|
+
duration: TOAST_DURATION,
|
|
282518
|
+
});
|
|
282519
|
+
}, [t, TOAST_DURATION]);
|
|
280846
282520
|
const { showDisclaimerModal, isAgreeing, userAgreement, hasUserAgreement, handleDisclaimerAgree, checkAgreementAndExecute, executePendingSubmit, } = useUserAgreement({ mentorId, tenantKey });
|
|
280847
282521
|
const { changeTab, activeTab, currentStreamingMessage, enabledGuidedPrompts, isStreaming, mentorName, messages, profileImage, sendMessage, setMessage, stopGenerating, uniqueMentorId, sessionId, startNewChat, enableSafetyDisclaimer, isPending, isLoadingChats, isConnected, refetchChats, } = useAdvancedChat({
|
|
280848
282522
|
mentorId,
|
|
@@ -280883,6 +282557,7 @@ function Chat({ mode = 'default', isPreviewMode = false, hasBorder = true, isInC
|
|
|
280883
282557
|
!(searchParams === null || searchParams === void 0 ? void 0 : searchParams.get('token'))),
|
|
280884
282558
|
mentorShareableToken: searchParams === null || searchParams === void 0 ? void 0 : searchParams.get('token'),
|
|
280885
282559
|
on402Error: handle402Error,
|
|
282560
|
+
onSpendCapError: handleSpendCapError,
|
|
280886
282561
|
cachedSessionId,
|
|
280887
282562
|
onStartNewChat: (sessionId) => {
|
|
280888
282563
|
dispatch(chatActions.updateSessionIds(sessionId));
|
|
@@ -333207,5 +334882,5 @@ var index_es = /*#__PURE__*/Object.freeze({
|
|
|
333207
334882
|
vectorsRatio: vectorsRatio
|
|
333208
334883
|
});
|
|
333209
334884
|
|
|
333210
|
-
export { AGENT_ACCESS_TAB_LABELS, AGENT_ANALYTICS_TAB_LABELS, AGENT_API_TAB_LABELS, AGENT_DATASETS_TAB_LABELS, AGENT_DISCLAIMERS_TAB_LABELS, AGENT_EMBED_TAB_LABELS, AGENT_EVALUATION_TAB_LABELS, AGENT_GRADER_TAB_LABELS, AGENT_HISTORY_TAB_LABELS, AGENT_HUMAN_SUPPORT_TAB_LABELS, AGENT_LLM_TAB_LABELS, AGENT_LTI_TAB_LABELS, AGENT_MEMORY_TAB_LABELS, AGENT_PICKER_LABELS, AGENT_PRIVACY_TAB_LABELS, AGENT_PROMPTS_TAB_LABELS, AGENT_SAFETY_TAB_LABELS, AGENT_SCREENSHARE_TAB_LABELS, AGENT_SEARCH_LABELS, AGENT_SETTINGS_TAB_LABELS, AGENT_SKILLS_TAB_LABELS, AGENT_TASKS_TAB_LABELS, AGENT_TOOLS_TAB_LABELS, AGENT_VOICE_TAB_LABELS, Account, AgentAccessTab, AgentAnalyticsTab, AgentApiTab, AgentCard, AgentDatasetsTab, AgentDisclaimersTab, AgentEmbedTab, AgentEmptyState, AgentEvaluationTab, AgentGraderTab, AgentHistoryTab, AgentHumanSupportTab, AgentLLMTab, AgentLtiTab, AgentMemoryTab, AgentPrivacyTab, AgentPromptsTab, AgentSafetyTab, AgentScreenShareTab, AgentSearch, AgentSearchFilters, AgentSearchInput, AgentSettingsProvider, AgentSettingsTab, AgentSkillsTab, AgentTasksTab, AgentToolsTab, AgentVoiceTab, AppSidebar, CATALOGS, Chat, ClientErrorPage, ConversationStarters$1 as ConversationStarters, CopyButton, CopyMentorModal, CourseBox, CourseContentLayout, CourseContentTabPage, CreatePathwayModal, CredentialDetailModal, CredentialMiniBox, DEFAULT_ANALYTICS_HUB_TABS, DEFAULT_LOCALE, DeleteMentorModal, EMPTY_PROGRAM_SETTINGS_FORM, EdxIframe, ErrorPage, GRADING_TOOL_NAME, GreetingMethod, McpTab, MediaBox, MentorSelectionGrid, NavBar, OrganizationTab, PLATFORM_SIDEBAR_FLYOUT_ITEM_COLOR, PLATFORM_SIDEBAR_FLYOUT_TITLE_COLOR, PLATFORM_SIDEBAR_NAV_ACTIVE_BG_OPEN, PLATFORM_SIDEBAR_NAV_MUTED, PathwayDetailModal, PlatformAccountSheet, PlatformSidebar, PlatformSidebarCollapsedLabelFlyout, PlatformSidebarCollapsedNavFlyout, PlatformSidebarFooter, PlatformSidebarNavDivider, PlatformSidebarNavSection, PlatformSidebarSubNavItem, ProfileInfoCards, ProfileTabs, ProgramDetailModal, ProjectsSidebarDropdown, SUPPORTED_LOCALES, SidebarInset, SidebarProvider, SidebarTrigger, SsoLogin, StarButton, UserProfileDropdown, UserProfileModal, WebContainersI18nProvider, formatDateString, getCatalog, getLLMProviderDetails, initializeLocalStorageWithObject, isValidLocale, resolveAgentPickerLabels, resolveAgentSearchLabels, resolveSettingsTabLabels, useAgentSearch, useAgentSearchWithPagination, useAgentSettings, useAgentStar, useGetChatDetails, usePlatformSidebarFooterActions, usePlatformSidebarNavCallback, useSidebar, useT, useTimeTracker, useWebContainersI18n, useWebContainersLocale };
|
|
334885
|
+
export { AGENT_ACCESS_TAB_LABELS, AGENT_ANALYTICS_TAB_LABELS, AGENT_API_TAB_LABELS, AGENT_DATASETS_TAB_LABELS, AGENT_DISCLAIMERS_TAB_LABELS, AGENT_EMBED_TAB_LABELS, AGENT_EVALUATION_TAB_LABELS, AGENT_GRADER_TAB_LABELS, AGENT_HISTORY_TAB_LABELS, AGENT_HUMAN_SUPPORT_TAB_LABELS, AGENT_LLM_TAB_LABELS, AGENT_LTI_TAB_LABELS, AGENT_MEMORY_TAB_LABELS, AGENT_PICKER_LABELS, AGENT_PRIVACY_TAB_LABELS, AGENT_PROMPTS_TAB_LABELS, AGENT_SAFETY_TAB_LABELS, AGENT_SCREENSHARE_TAB_LABELS, AGENT_SEARCH_LABELS, AGENT_SETTINGS_TAB_LABELS, AGENT_SKILLS_TAB_LABELS, AGENT_SPEND_CAPS_TAB_LABELS, AGENT_TASKS_TAB_LABELS, AGENT_TOOLS_TAB_LABELS, AGENT_VOICE_TAB_LABELS, Account, AgentAccessTab, AgentAnalyticsTab, AgentApiTab, AgentCard, AgentDatasetsTab, AgentDisclaimersTab, AgentEmbedTab, AgentEmptyState, AgentEvaluationTab, AgentGraderTab, AgentHistoryTab, AgentHumanSupportTab, AgentLLMTab, AgentLtiTab, AgentMemoryTab, AgentPrivacyTab, AgentPromptsTab, AgentSafetyTab, AgentScreenShareTab, AgentSearch, AgentSearchFilters, AgentSearchInput, AgentSettingsProvider, AgentSettingsTab, AgentSkillsTab, AgentSpendCapsTab, AgentTasksTab, AgentToolsTab, AgentVoiceTab, AppSidebar, CATALOGS, Chat, ClientErrorPage, ConversationStarters$1 as ConversationStarters, CopyButton, CopyMentorModal, CourseBox, CourseContentLayout, CourseContentTabPage, CreatePathwayModal, CredentialDetailModal, CredentialMiniBox, DEFAULT_ANALYTICS_HUB_TABS, DEFAULT_LOCALE, DeleteMentorModal, EMPTY_PROGRAM_SETTINGS_FORM, EdxIframe, ErrorPage, GRADING_TOOL_NAME, GreetingMethod, McpTab, MediaBox, MentorSelectionGrid, NavBar, OrganizationTab, PLATFORM_SIDEBAR_FLYOUT_ITEM_COLOR, PLATFORM_SIDEBAR_FLYOUT_TITLE_COLOR, PLATFORM_SIDEBAR_NAV_ACTIVE_BG_OPEN, PLATFORM_SIDEBAR_NAV_MUTED, PathwayDetailModal, PlatformAccountSheet, PlatformSidebar, PlatformSidebarCollapsedLabelFlyout, PlatformSidebarCollapsedNavFlyout, PlatformSidebarFooter, PlatformSidebarNavDivider, PlatformSidebarNavSection, PlatformSidebarSubNavItem, ProfileInfoCards, ProfileTabs, ProgramDetailModal, ProjectsSidebarDropdown, SPEND_CAPS_SUB_TABS, SUPPORTED_LOCALES, SidebarInset, SidebarProvider, SidebarTrigger, SpendCapUsage, SsoLogin, StarButton, UserProfileDropdown, UserProfileModal, WebContainersI18nProvider, formatDateString, getCatalog, getLLMProviderDetails, initializeLocalStorageWithObject, isValidLocale, resolveAgentPickerLabels, resolveAgentSearchLabels, resolveSettingsTabLabels, useAgentSearch, useAgentSearchWithPagination, useAgentSettings, useAgentStar, useGetChatDetails, usePlatformSidebarFooterActions, usePlatformSidebarNavCallback, useSidebar, useT, useTimeTracker, useWebContainersI18n, useWebContainersLocale };
|
|
333211
334886
|
//# sourceMappingURL=index.esm.js.map
|