diff --git a/doc/screenshots/pr-6381/aws-discovery-candidates.png b/doc/screenshots/pr-6381/aws-discovery-candidates.png new file mode 100644 index 00000000000..9006d175cf6 Binary files /dev/null and b/doc/screenshots/pr-6381/aws-discovery-candidates.png differ diff --git a/doc/screenshots/pr-6381/provider-vaults-tab.png b/doc/screenshots/pr-6381/provider-vaults-tab.png new file mode 100644 index 00000000000..6d304c20422 Binary files /dev/null and b/doc/screenshots/pr-6381/provider-vaults-tab.png differ diff --git a/doc/screenshots/pr-6381/remove-provider-vault-confirmation.png b/doc/screenshots/pr-6381/remove-provider-vault-confirmation.png new file mode 100644 index 00000000000..40a4c81a06e Binary files /dev/null and b/doc/screenshots/pr-6381/remove-provider-vault-confirmation.png differ diff --git a/ui/storybook/.storybook/preview.tsx b/ui/storybook/.storybook/preview.tsx index df150978b35..011ec14b5b0 100644 --- a/ui/storybook/.storybook/preview.tsx +++ b/ui/storybook/.storybook/preview.tsx @@ -22,6 +22,8 @@ import { storybookProjects, storybookSecretAccessEvents, storybookSecretBindings, + storybookSecretProviderConfigs, + storybookSecretProviderDiscoveryPreview, storybookSecretProviderHealth, storybookSecretProviders, storybookSecrets, @@ -187,6 +189,20 @@ function installStorybookApiFixtures() { return Response.json(storybookSecretProviderHealth); } + const secretProviderConfigsMatch = url.pathname.match( + /^\/api\/companies\/([^/]+)\/secret-provider-configs$/, + ); + if (secretProviderConfigsMatch) { + return Response.json(storybookSecretProviderConfigs); + } + + const secretProviderConfigDiscoveryPreviewMatch = url.pathname.match( + /^\/api\/companies\/([^/]+)\/secret-provider-configs\/discovery\/preview$/, + ); + if (secretProviderConfigDiscoveryPreviewMatch && init?.method?.toUpperCase() === "POST") { + return Response.json(storybookSecretProviderDiscoveryPreview); + } + const secretUsageMatch = url.pathname.match(/^\/api\/secrets\/([^/]+)\/usage$/); if (secretUsageMatch) { const [, secretId] = secretUsageMatch; diff --git a/ui/storybook/fixtures/paperclipData.ts b/ui/storybook/fixtures/paperclipData.ts index 0984f9d5afa..d798c9ee039 100644 --- a/ui/storybook/fixtures/paperclipData.ts +++ b/ui/storybook/fixtures/paperclipData.ts @@ -7,6 +7,7 @@ import type { Company, CompanySecret, CompanySecretBinding, + CompanySecretProviderConfig, DashboardSummary, ExecutionWorkspace, Goal, @@ -15,6 +16,7 @@ import type { IssueLabel, Project, SecretAccessEvent, + SecretProviderConfigDiscoveryPreviewResult, SecretProviderDescriptor, SidebarBadges, WorkspaceRuntimeService, @@ -1324,6 +1326,98 @@ export const storybookSecretProviders: SecretProviderDescriptor[] = [ { id: "vault", label: "HashiCorp Vault", requiresExternalRef: false }, ]; +export const storybookSecretProviderConfigs: CompanySecretProviderConfig[] = [ + { + id: "provider-config-local", + companyId: "company-storybook", + provider: "local_encrypted", + displayName: "Local encrypted default", + status: "ready", + isDefault: true, + config: { backupReminderAcknowledged: true }, + healthStatus: "ready", + healthCheckedAt: recent(45), + healthMessage: "Local encrypted provider is healthy.", + healthDetails: null, + disabledAt: null, + createdByAgentId: null, + createdByUserId: "user-board", + createdAt: recent(2_400), + updatedAt: recent(45), + }, + { + id: "provider-config-aws-prod", + companyId: "company-storybook", + provider: "aws_secrets_manager", + displayName: "AWS production", + status: "warning", + isDefault: false, + config: { + region: "us-east-1", + namespace: "prod-use1", + secretNamePrefix: "paperclip", + kmsKeyId: "alias/paperclip-secrets", + ownerTag: "platform", + environmentTag: "production", + }, + healthStatus: "warning", + healthCheckedAt: recent(18), + healthMessage: "Connected; KMS key rotation policy not yet enforced.", + healthDetails: { + code: "kms_rotation_policy", + message: "Connected; KMS key rotation policy not yet enforced.", + guidance: ["Enable automatic key rotation before using this vault for production agents."], + }, + disabledAt: null, + createdByAgentId: null, + createdByUserId: "user-board", + createdAt: recent(1_800), + updatedAt: recent(18), + }, +]; + +export const storybookSecretProviderDiscoveryPreview: SecretProviderConfigDiscoveryPreviewResult = { + provider: "aws_secrets_manager", + nextToken: null, + sampledSecretCount: 6, + skippedForeignPaperclipSampleCount: 1, + warnings: ["Skipped 1 Paperclip-managed AWS secret from a different deployment namespace."], + candidates: [ + { + provider: "aws_secrets_manager", + displayName: "AWS production", + config: { + region: "us-east-1", + namespace: "prod-use1", + secretNamePrefix: "paperclip", + kmsKeyId: "alias/paperclip-secrets", + ownerTag: "platform", + environmentTag: "production", + }, + sampleCount: 5, + samples: [ + { + name: "paperclip/prod-use1/company-storybook/openai_api_key", + hasKmsKey: true, + tagKeys: ["paperclip:managed-by", "paperclip:environment", "paperclip:provider-owner"], + }, + ], + signals: { + namespace: "prod-use1", + secretNamePrefix: "paperclip", + environmentTag: "production", + ownerTag: "platform", + kmsKeyId: "alias/paperclip-secrets", + hasKmsKey: true, + sampleCount: 5, + paperclipManagedSampleCount: 5, + skippedForeignPaperclipSampleCount: 1, + }, + warnings: [], + }, + ], +}; + export const storybookSecrets: CompanySecret[] = [ { id: "secret-openai", diff --git a/ui/storybook/stories/secrets.stories.tsx b/ui/storybook/stories/secrets.stories.tsx index 5c3d09ea5b8..0b787829a9e 100644 --- a/ui/storybook/stories/secrets.stories.tsx +++ b/ui/storybook/stories/secrets.stories.tsx @@ -22,7 +22,6 @@ if (typeof window !== "undefined") { function StorybookSecretsFixtures({ children }: { children: ReactNode }) { const queryClient = useQueryClient(); // Seed query caches synchronously so children hydrate from cache on first render. - queryClient.setQueryData(queryKeys.companies.all, storybookCompanies); queryClient.setQueryData(queryKeys.secrets.list(COMPANY_ID), storybookSecrets); const { selectedCompanyId, setSelectedCompanyId } = useCompany();