Rework of secret imports on overview page

This commit is contained in:
carlosmonastyrski
2025-03-18 16:26:15 -03:00
parent e67a8f9c05
commit 633a2ae985
9 changed files with 243 additions and 236 deletions
@@ -68,15 +68,9 @@ import { DashboardSecretsOrderBy } from "@app/hooks/api/dashboard/types";
import { OrderByDirection } from "@app/hooks/api/generic/types";
import { useUpdateFolderBatch } from "@app/hooks/api/secretFolders/queries";
import { TUpdateFolderBatchDTO } from "@app/hooks/api/secretFolders/types";
import { TSecretImportMultiEnvData } from "@app/hooks/api/secretImports/types";
import { SecretType, SecretV3RawSanitized, TSecretFolder } from "@app/hooks/api/types";
import { ProjectType, ProjectVersion } from "@app/hooks/api/workspace/types";
import {
useDynamicSecretOverview,
useFolderOverview,
useSecretImportOverview,
useSecretOverview
} from "@app/hooks/utils";
import { useDynamicSecretOverview, useFolderOverview, useSecretOverview } from "@app/hooks/utils";
import { FolderForm } from "../SecretDashboardPage/components/ActionBar/FolderForm";
import { CreateSecretForm } from "./components/CreateSecretForm";
@@ -195,33 +189,12 @@ export const OverviewPage = () => {
setVisibleEnvs(userAvailableEnvs);
}, [userAvailableEnvs]);
const {
secretImports,
isImportedSecretPresentInEnv,
getImportedSecretByKey,
getEnvImportedSecretKeyCount
} = useGetImportedSecretsAllEnvs({
projectId: workspaceId,
path: secretPath,
environments: (userAvailableEnvs || []).map(({ slug }) => slug)
});
const secretImportsData = useMemo(
() =>
(secretImports?.map((s) => s.data as TSecretImportMultiEnvData[]) ?? [])
?.flatMap((s) => s ?? [])
?.filter((secretImport) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.SecretImports, {
environment: secretImport.currentEnv,
secretPath
})
)
),
[secretImports]
);
const { uniqueEnvSecretPaths, isSecretImportPresent } =
useSecretImportOverview(secretImportsData);
const { isImportedSecretPresentInEnv, getImportedSecretByKey, getEnvImportedSecretKeyCount } =
useGetImportedSecretsAllEnvs({
projectId: workspaceId,
path: secretPath,
environments: (userAvailableEnvs || []).map(({ slug }) => slug)
});
const { isPending: isOverviewLoading, data: overview } = useGetProjectSecretsOverview(
{
@@ -233,6 +206,7 @@ export const OverviewPage = () => {
includeFolders: filter.folder,
includeDynamicSecrets: filter.dynamic,
includeSecrets: filter.secret,
includeImports: filter.import,
search: debouncedSearchFilter,
limit,
offset
@@ -244,15 +218,29 @@ export const OverviewPage = () => {
secrets,
folders,
dynamicSecrets,
imports,
totalFolderCount,
totalSecretCount,
totalDynamicSecretCount,
totalImportCount,
totalCount = 0,
totalUniqueFoldersInPage,
totalUniqueSecretsInPage,
totalUniqueSecretImportsInPage,
totalUniqueDynamicSecretsInPage
} = overview ?? {};
const importsShaped = imports
?.filter((el) => !el.isReserved)
?.map(({ importPath, importEnv }) => ({ importPath, importEnv }))
.filter(
(el, index, self) =>
index ===
self.findIndex(
(item) => item.importPath === el.importPath && item.importEnv.slug === el.importEnv.slug
)
);
useResetPageHelper({
totalCount,
offset,
@@ -693,7 +681,6 @@ export const OverviewPage = () => {
<SecretV2MigrationSection />
</div>
);
return (
<div className="">
<Helmet>
@@ -1099,16 +1086,14 @@ export const OverviewPage = () => {
/>
))}
{filter.import &&
Object.entries(uniqueEnvSecretPaths).map(([key, secretImportsAllEnvs]) => (
importsShaped &&
importsShaped?.length > 0 &&
importsShaped?.map((item, index) => (
<SecretOverviewImportListView
secretImport={secretImportsAllEnvs}
isImportedSecretPresentInEnv={isSecretImportPresent}
secretImport={item}
environments={visibleEnvs}
key={`overview-${key}`}
scrollOffset={debouncedScrollOffset}
allSecretImports={(
secretImportsData?.flatMap((s) => s ?? []) ?? []
).filter(Boolean)}
key={`overview-secret-input-${index + 1}`}
allSecretImports={imports}
/>
))}
{secKeys.map((key, index) => (
@@ -1134,7 +1119,8 @@ export const OverviewPage = () => {
(page * perPage > totalCount ? totalCount % perPage : perPage) -
(totalUniqueFoldersInPage || 0) -
(totalUniqueDynamicSecretsInPage || 0) -
(totalUniqueSecretsInPage || 0),
(totalUniqueSecretsInPage || 0) -
(totalUniqueSecretImportsInPage || 0),
0
)}
/>
@@ -1174,6 +1160,7 @@ export const OverviewPage = () => {
dynamicSecretCount={totalDynamicSecretCount}
secretCount={totalSecretCount}
folderCount={totalFolderCount}
importCount={totalImportCount}
/>
}
className="rounded-b-md border-t border-solid border-t-mineshaft-600"
@@ -1,149 +1,76 @@
import { faCheck, faFileImport, faKey, faXmark } from "@fortawesome/free-solid-svg-icons";
import { faCheck, faFileImport, faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { twMerge } from "tailwind-merge";
import { EmptyState, SecretInput, TableContainer, Td, Tr } from "@app/components/v2";
import { useToggle } from "@app/hooks";
import { TSecretImportMultiEnvData } from "@app/hooks/api/secretImports/types";
import { Td, Tr } from "@app/components/v2";
import { TSecretImport, WorkspaceEnv } from "@app/hooks/api/types";
import { EnvFolderIcon } from "@app/pages/secret-manager/SecretDashboardPage/components/SecretImportListView/SecretImportItem";
import { computeImportedSecretRows } from "@app/pages/secret-manager/SecretDashboardPage/components/SecretImportListView/SecretImportListView";
type Props = {
secretImport: TSecretImportMultiEnvData;
secretImport: { importPath: string; importEnv: WorkspaceEnv };
environments: { name: string; slug: string }[];
isImportedSecretPresentInEnv: (
sourceEnv: string,
targetEnv: string,
secretPath: string
) => boolean;
scrollOffset: number;
allSecretImports: TSecretImportMultiEnvData[];
allSecretImports?: TSecretImport[];
};
export const SecretOverviewImportListView = ({
secretImport,
environments = [],
isImportedSecretPresentInEnv,
scrollOffset,
allSecretImports = []
}: Props) => {
const [isFormExpanded, setIsFormExpanded] = useToggle();
const environmentImportDetails = secretImport.environmentInfo;
const totalCols = environments.length + 1;
const isSecretPresentInEnv = (envSlug: string) => {
return allSecretImports.some((item) => {
if (item.isReplication) {
const reservedItem = allSecretImports.find((element) =>
element.importPath.includes(`__reserve_replication_${item.id}`)
);
// If the reserved item exists, check if the envSlug matches
if (reservedItem) {
return reservedItem.environment === envSlug;
}
} else {
// If the item is not replication, check if the envSlug matches directly
return item.environment === envSlug;
}
return false;
});
};
const computeImportedSecrets =
allSecretImports.length > 0
? computeImportedSecretRows(
environmentImportDetails.slug,
secretImport.secretPath,
allSecretImports
)
: [];
return (
<>
<Tr
isHoverable
isSelectable
onClick={() => setIsFormExpanded.toggle()}
className={`group ${isFormExpanded ? "border-t-2 border-mineshaft-500" : ""}`}
>
<Td className="sticky left-0 z-10 bg-mineshaft-800 bg-clip-padding px-0 py-0 group-hover:bg-mineshaft-700">
<div className="group flex cursor-pointer">
<div className="flex w-11 items-center py-2 pl-5 text-green-700">
<FontAwesomeIcon icon={faFileImport} />
</div>
<div className="flex flex-grow items-center py-2 pl-4 pr-2">
<EnvFolderIcon
env={environmentImportDetails.slug || ""}
secretPath={secretImport.secretPath || ""}
/>
</div>
<Tr className="group">
<Td className="sticky left-0 z-10 bg-mineshaft-800 bg-clip-padding px-0 py-0 group-hover:bg-mineshaft-700">
<div className="group flex cursor-pointer">
<div className="flex w-11 items-center py-2 pl-5 text-green-700">
<FontAwesomeIcon icon={faFileImport} />
</div>
</Td>
{environments.map(({ slug }, i) => {
const isPresent = isImportedSecretPresentInEnv(
slug,
secretImport.environment,
secretImport.secretPath
);
return (
<Td
key={`sec-overview-${slug}-${i + 1}-value`}
className={twMerge(
"px-0 py-0 group-hover:bg-mineshaft-700",
isPresent ? "text-green-600" : "text-red-600"
)}
>
<div className="h-full w-full border-r border-mineshaft-600 px-5 py-[0.85rem]">
<div className="flex justify-center">
<FontAwesomeIcon
// eslint-disable-next-line no-nested-ternary
icon={
isImportedSecretPresentInEnv(
slug,
secretImport.environment,
secretImport.secretPath
)
? faCheck
: faXmark
}
/>
</div>
</div>
</Td>
);
})}
</Tr>
{isFormExpanded && (
<Tr>
<div className="flex flex-grow items-center py-2 pl-4 pr-2">
<EnvFolderIcon
env={secretImport.importEnv.slug || ""}
secretPath={secretImport.importPath || ""}
/>
</div>
</div>
</Td>
{environments.map(({ slug }, i) => {
const isPresent = isSecretPresentInEnv(slug);
return (
<Td
colSpan={totalCols}
className={`bg-bunker-600 px-0 py-0 ${
isFormExpanded && "border-b-2 border-mineshaft-500"
}`}
key={`sec-overview-${slug}-${i + 1}-value`}
className={twMerge(
"px-0 py-0 group-hover:bg-mineshaft-700",
isPresent ? "text-green-600" : "text-red-600"
)}
>
<div
className="ml-2 p-2"
style={{
marginLeft: scrollOffset,
width: "100%"
}}
>
<TableContainer>
<table className="secret-table">
<thead>
<tr>
<td style={{ padding: "0.25rem 1rem" }}>Key</td>
<td style={{ padding: "0.25rem 1rem" }}>Value</td>
{/* <td style={{ padding: "0.25rem 1rem" }}>Override</td> */}
</tr>
</thead>
<tbody>
{computeImportedSecrets?.length === 0 && (
<tr>
<td colSpan={3}>
<EmptyState title="No secrets found" icon={faKey} />
</td>
</tr>
)}
{computeImportedSecrets.map(({ key, value }, index) => (
<tr key={`${key}-${index + 1}`}>
<td className="h-10" style={{ padding: "0.25rem 1rem" }}>
{key}
</td>
<td className="h-10" style={{ padding: "0.25rem 1rem" }}>
<SecretInput value={value} isReadOnly />
</td>
</tr>
))}
</tbody>
</table>
</TableContainer>
<div className="h-full w-full border-r border-mineshaft-600 px-5 py-[0.85rem]">
<div className="flex justify-center">
<FontAwesomeIcon
// eslint-disable-next-line no-nested-ternary
icon={isSecretPresentInEnv(slug) ? faCheck : faXmark}
/>
</div>
</div>
</Td>
</Tr>
)}
</>
);
})}
</Tr>
);
};