feat(secrets-ui): improve warning message table

This commit is contained in:
carlosmonastyrski
2025-04-29 14:19:52 -03:00
parent e959ed7fab
commit c47d76a6c7
6 changed files with 70 additions and 46 deletions

View File

@@ -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) {

View File

@@ -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 = {

View File

@@ -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<string, Record<string, SecretV3RawSanitized>>;
};
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))) && (
<CollapsibleSecretImports
importedBy={importedBy}
importedBy={importedBy || []}
secretsToDelete={secretsToDeleteKeys}
usedBySecretSyncs={usedBySecretSyncsFiltered}
/>

View File

@@ -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: {

View File

@@ -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<CollapsibleSecretImportsProps> =
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<CollapsibleSecretImportsProps> =
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<CollapsibleSecretImportsProps> =
className="cursor-pointer hover:bg-mineshaft-700"
title={
item.type === ItemType.SecretSync
? "Navigate to Secret Syncs"
? "Navigate to Secret Sync"
: `Navigate to ${item.path}`
}
>
<Td>
<FontAwesomeIcon
icon={
item.type === ItemType.Secret
? faKey
: item.type === ItemType.Folder
? faFileImport
: faSync
}
className={`h-4 w-4 ${
item.type === ItemType.Secret
? "text-gray-400"
: item.type === ItemType.Folder
? "text-green-700"
: "text-blue-500"
}`}
aria-hidden="true"
/>
<Tooltip
className="max-w-md"
content={item.tooltipText}
isDisabled={!item.tooltipText}
>
<FontAwesomeIcon
icon={
item.type === ItemType.Secret
? faKey
: item.type === ItemType.Folder
? faFileImport
: faSync
}
className={`h-4 w-4 ${
item.type === ItemType.Secret
? "text-gray-400"
: item.type === ItemType.Folder
? "text-green-700"
: "text-blue-500"
}`}
aria-hidden="true"
/>
</Tooltip>
</Td>
<Td className="px-4">{item.environment.name}</Td>
<Td className="truncate px-4">{truncatePath(item.path)}</Td>

View File

@@ -8,6 +8,7 @@ import { DeleteActionModal } from "@app/components/v2";
import { usePopUp } from "@app/hooks";
import { useCreateSecretV3, useDeleteSecretV3, useUpdateSecretV3 } from "@app/hooks/api";
import { dashboardKeys } from "@app/hooks/api/dashboard/queries";
import { UsedBySecretSyncs } from "@app/hooks/api/dashboard/types";
import { secretApprovalRequestKeys } from "@app/hooks/api/secretApprovalRequest/queries";
import { secretKeys } from "@app/hooks/api/secrets/queries";
import { SecretType, SecretV3RawSanitized } from "@app/hooks/api/secrets/types";
@@ -29,7 +30,7 @@ type Props = {
tags?: WsTag[];
isVisible?: boolean;
isProtectedBranch?: boolean;
usedBySecretSyncs?: { environment: string; name: string; destination: string }[];
usedBySecretSyncs?: UsedBySecretSyncs[];
importedBy?: {
environment: { name: string; slug: string };
folders: {
@@ -385,8 +386,8 @@ export const SecretListView = ({
onDeleteApproved={handleSecretDelete}
buttonText="Delete Secret"
formContent={
importedBy &&
importedBy.length > 0 && (
((importedBy && importedBy.length > 0) ||
(usedBySecretSyncs && usedBySecretSyncs?.length > 0)) && (
<CollapsibleSecretImports
importedBy={importedBy}
usedBySecretSyncs={usedBySecretSyncs}