From c47d76a6c7eac59951d7a6760da0da6b71c5447a Mon Sep 17 00:00:00 2001 From: carlosmonastyrski Date: Tue, 29 Apr 2025 14:19:52 -0300 Subject: [PATCH] feat(secrets-ui): improve warning message table --- .../src/server/routes/v1/dashboard-router.ts | 19 ++++-- frontend/src/hooks/api/dashboard/types.ts | 12 +++- .../SelectionPanel/SelectionPanel.tsx | 10 +-- .../components/ActionBar/ActionBar.tsx | 3 +- .../CollapsibleSecretImports.tsx | 65 ++++++++++--------- .../SecretListView/SecretListView.tsx | 7 +- 6 files changed, 70 insertions(+), 46 deletions(-) diff --git a/backend/src/server/routes/v1/dashboard-router.ts b/backend/src/server/routes/v1/dashboard-router.ts index 146a31cf2..373e2d51f 100644 --- a/backend/src/server/routes/v1/dashboard-router.ts +++ b/backend/src/server/routes/v1/dashboard-router.ts @@ -171,7 +171,9 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => { .object({ name: z.string(), destination: z.string(), - environment: z.string() + environment: z.string(), + id: z.string(), + path: z.string() }) .array() .optional(), @@ -509,7 +511,8 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => { } } - const usedBySecretSyncs: { name: string; destination: string; environment: string }[] = []; + const usedBySecretSyncs: { name: string; destination: string; environment: string; id: string; path: string }[] = + []; for await (const environment of environments) { const secretSyncs = await server.services.secretSync.listSecretSyncsBySecretPath( { projectId, secretPath, environment }, @@ -519,7 +522,9 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => { usedBySecretSyncs.push({ name: sync.name, destination: sync.destination, - environment + environment, + id: sync.id, + path: sync.folder?.path || "/" }); }); } @@ -640,7 +645,9 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => { .object({ name: z.string(), destination: z.string(), - environment: z.string() + environment: z.string(), + id: z.string(), + path: z.string() }) .array() .optional(), @@ -945,7 +952,9 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => { const usedBySecretSyncs = secretSyncs.map((sync) => ({ name: sync.name, destination: sync.destination, - environment: sync.environment?.name || environment + environment: sync.environment?.name || environment, + id: sync.id, + path: sync.folder?.path || "/" })); if (secrets?.length || secretRotations?.length) { diff --git a/frontend/src/hooks/api/dashboard/types.ts b/frontend/src/hooks/api/dashboard/types.ts index 21e4a629c..78c56394f 100644 --- a/frontend/src/hooks/api/dashboard/types.ts +++ b/frontend/src/hooks/api/dashboard/types.ts @@ -25,10 +25,18 @@ export type DashboardProjectSecretsOverviewResponse = { totalUniqueFoldersInPage: number; totalUniqueSecretImportsInPage: number; importedByEnvs?: { environment: string; importedBy: ProjectSecretsImportedBy[] }[]; - usedBySecretSyncs?: { name: string; destination: string; environment: string }[]; + usedBySecretSyncs?: UsedBySecretSyncs[]; totalUniqueSecretRotationsInPage: number; }; +export type UsedBySecretSyncs = { + name: string; + destination: string; + environment: string; + id: string; + path: string; +}; + export type DashboardProjectSecretsDetailsResponse = { imports?: TSecretImport[]; folders?: TSecretFolder[]; @@ -44,7 +52,7 @@ export type DashboardProjectSecretsDetailsResponse = { totalSecretRotationCount?: number; totalCount: number; importedBy?: ProjectSecretsImportedBy[]; - usedBySecretSyncs?: { name: string; destination: string; environment: string }[]; + usedBySecretSyncs?: UsedBySecretSyncs[]; }; export type ProjectSecretsImportedBy = { diff --git a/frontend/src/pages/secret-manager/OverviewPage/components/SelectionPanel/SelectionPanel.tsx b/frontend/src/pages/secret-manager/OverviewPage/components/SelectionPanel/SelectionPanel.tsx index 464c19dcb..91c412a6a 100644 --- a/frontend/src/pages/secret-manager/OverviewPage/components/SelectionPanel/SelectionPanel.tsx +++ b/frontend/src/pages/secret-manager/OverviewPage/components/SelectionPanel/SelectionPanel.tsx @@ -15,7 +15,7 @@ import { import { ProjectPermissionSecretActions } from "@app/context/ProjectPermissionContext/types"; import { usePopUp } from "@app/hooks"; import { useDeleteFolder, useDeleteSecretBatch } from "@app/hooks/api"; -import { ProjectSecretsImportedBy } from "@app/hooks/api/dashboard/types"; +import { ProjectSecretsImportedBy, UsedBySecretSyncs } from "@app/hooks/api/dashboard/types"; import { SecretType, SecretV3RawSanitized, @@ -38,7 +38,7 @@ type Props = { [EntryType.SECRET]: Record>; }; importedBy?: ProjectSecretsImportedBy[] | null; - usedBySecretSyncs?: { name: string; destination: string; environment: string }[]; + usedBySecretSyncs?: UsedBySecretSyncs[]; secretsToDeleteKeys: string[]; }; @@ -261,10 +261,10 @@ export const SelectionPanel = ({ onChange={(isOpen) => handlePopUpToggle("bulkDeleteEntries", isOpen)} onDeleteApproved={handleBulkDelete} formContent={ - importedBy && - importedBy.some((element) => element.folders.length > 0) && ( + ((usedBySecretSyncsFiltered && usedBySecretSyncsFiltered.length > 0) || + (importedBy && importedBy.some((element) => element.folders.length > 0))) && ( diff --git a/frontend/src/pages/secret-manager/SecretDashboardPage/components/ActionBar/ActionBar.tsx b/frontend/src/pages/secret-manager/SecretDashboardPage/components/ActionBar/ActionBar.tsx index 95a11251d..13b6d45b7 100644 --- a/frontend/src/pages/secret-manager/SecretDashboardPage/components/ActionBar/ActionBar.tsx +++ b/frontend/src/pages/secret-manager/SecretDashboardPage/components/ActionBar/ActionBar.tsx @@ -69,6 +69,7 @@ import { dashboardKeys, fetchDashboardProjectSecretsByKeys } from "@app/hooks/api/dashboard/queries"; +import { UsedBySecretSyncs } from "@app/hooks/api/dashboard/types"; import { secretApprovalRequestKeys } from "@app/hooks/api/secretApprovalRequest/queries"; import { fetchProjectSecrets, secretKeys } from "@app/hooks/api/secrets/queries"; import { ApiErrorTypes, SecretType, TApiErrors, WsTag } from "@app/hooks/api/types"; @@ -113,7 +114,7 @@ type Props = { onVisibilityToggle: () => void; onToggleRowType: (rowType: RowType) => void; onClickRollbackMode: () => void; - usedBySecretSyncs?: { environment: string; name: string; destination: string }[]; + usedBySecretSyncs?: UsedBySecretSyncs[]; importedBy?: { environment: { name: string; slug: string }; folders: { diff --git a/frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/CollapsibleSecretImports.tsx b/frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/CollapsibleSecretImports.tsx index 773364ef3..a53a4cf24 100644 --- a/frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/CollapsibleSecretImports.tsx +++ b/frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/CollapsibleSecretImports.tsx @@ -3,8 +3,9 @@ import React, { useMemo } from "react"; import { faFileImport, faKey, faSync, faWarning } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { Table, TBody, Td, Th, THead, Tr } from "@app/components/v2"; +import { Table, TBody, Td, Th, THead, Tooltip, Tr } from "@app/components/v2"; import { useWorkspace } from "@app/context"; +import { UsedBySecretSyncs } from "@app/hooks/api/dashboard/types"; enum ItemType { Folder = "Folder", @@ -19,6 +20,8 @@ interface FlatItem { reference: string; id: string; environment: { name: string; slug: string }; + tooltipText?: string; + destination?: string; } interface CollapsibleSecretImportsProps { @@ -30,13 +33,7 @@ interface CollapsibleSecretImportsProps { isImported: boolean; }[]; }[]; - usedBySecretSyncs?: - | { - name: string; - destination: string; - environment: string; - }[] - | null; + usedBySecretSyncs?: UsedBySecretSyncs[] | null; secretsToDelete: string[]; onlyReferences?: boolean; } @@ -65,7 +62,7 @@ export const CollapsibleSecretImports: React.FC = const handlePathClick = (item: FlatItem) => { if (item.type === ItemType.SecretSync) { window.open( - `/secret-manager/${currentWorkspace.id}/integrations?selectedTab=secret-syncs`, + `/secret-manager/${currentWorkspace.id}/integrations/secret-syncs/${item.destination}/${item.id}`, "_blank", "noopener,noreferrer" ); @@ -128,10 +125,12 @@ export const CollapsibleSecretImports: React.FC = usedBySecretSyncs?.forEach((syncItem) => { items.push({ type: ItemType.SecretSync, - path: syncItem.destination, - id: `secret-sync-${syncItem.name}-${syncItem.destination}`, + destination: syncItem.destination, + path: syncItem.path, + id: syncItem.id, reference: "Secret Sync", - environment: { name: syncItem.environment, slug: "" } + environment: { name: syncItem.environment, slug: "" }, + tooltipText: `Currently used by Secret Sync: ${syncItem.name}` }); }); @@ -222,28 +221,34 @@ export const CollapsibleSecretImports: React.FC = className="cursor-pointer hover:bg-mineshaft-700" title={ item.type === ItemType.SecretSync - ? "Navigate to Secret Syncs" + ? "Navigate to Secret Sync" : `Navigate to ${item.path}` } > -