Feat: Visualize imported folders in overview page

This commit is contained in:
Daniel Hougaard
2024-03-20 12:54:44 +01:00
parent ffc7249c7c
commit 1d465a50c3
4 changed files with 56 additions and 25 deletions
@@ -13,9 +13,9 @@ import { secretSnapshotKeys } from "../secretSnapshots/queries";
import { import {
TCreateFolderDTO, TCreateFolderDTO,
TDeleteFolderDTO, TDeleteFolderDTO,
TFetchProjectFoldersResponse,
TGetFoldersByEnvDTO, TGetFoldersByEnvDTO,
TGetProjectFoldersDTO, TGetProjectFoldersDTO,
TSecretFolder,
TUpdateFolderDTO TUpdateFolderDTO
} from "./types"; } from "./types";
@@ -25,14 +25,14 @@ export const folderQueryKeys = {
}; };
const fetchProjectFolders = async (workspaceId: string, environment: string, path = "/") => { const fetchProjectFolders = async (workspaceId: string, environment: string, path = "/") => {
const { data } = await apiRequest.get<{ folders: TSecretFolder[] }>("/api/v1/folders", { const { data } = await apiRequest.get<TFetchProjectFoldersResponse>("/api/v1/folders", {
params: { params: {
workspaceId, workspaceId,
environment, environment,
path path
} }
}); });
return data.folders; return data;
}; };
export const useGetProjectFolders = ({ export const useGetProjectFolders = ({
@@ -43,9 +43,9 @@ export const useGetProjectFolders = ({
}: TGetProjectFoldersDTO & { }: TGetProjectFoldersDTO & {
options?: Omit< options?: Omit<
UseQueryOptions< UseQueryOptions<
TSecretFolder[], TFetchProjectFoldersResponse,
unknown, unknown,
TSecretFolder[], TFetchProjectFoldersResponse,
ReturnType<typeof folderQueryKeys.getSecretFolders> ReturnType<typeof folderQueryKeys.getSecretFolders>
>, >,
"queryKey" | "queryFn" "queryKey" | "queryFn"
@@ -74,7 +74,7 @@ export const useGetFoldersByEnv = ({
const folderNames = useMemo(() => { const folderNames = useMemo(() => {
const names = new Set<string>(); const names = new Set<string>();
folders?.forEach(({ data }) => { folders?.forEach(({ data }) => {
data?.forEach(({ name }) => { data?.folders?.forEach(({ name }) => {
names.add(name); names.add(name);
}); });
}); });
@@ -85,16 +85,34 @@ export const useGetFoldersByEnv = ({
(name: string, env: string) => { (name: string, env: string) => {
const selectedEnvIndex = environments.indexOf(env); const selectedEnvIndex = environments.indexOf(env);
if (selectedEnvIndex !== -1) { if (selectedEnvIndex !== -1) {
return Boolean( const isPresent = folders?.[selectedEnvIndex]?.data?.folders.find(
folders?.[selectedEnvIndex]?.data?.find(({ name: folderName }) => folderName === name) ({ name: folderName }) => folderName === name
); );
return Boolean(isPresent);
} }
return false; return false;
}, },
[(folders || []).map((folder) => folder.data)] [(folders || []).map((folder) => folder.data)]
); );
return { folders, folderNames, isFolderPresentInEnv }; const isImportedFolderPresentInEnv = useCallback(
(name: string, env: string) => {
const selectedEnvIndex = environments.indexOf(env);
if (selectedEnvIndex !== -1) {
const isPresent = folders?.[selectedEnvIndex]?.data?.importedFolders.find(
({ importPath }) => importPath === `/${name}`
);
return Boolean(isPresent);
}
return false;
},
[(folders || []).map((folder) => folder.data)]
);
return { folders, folderNames, isFolderPresentInEnv, isImportedFolderPresentInEnv };
}; };
export const useCreateFolder = () => { export const useCreateFolder = () => {
@@ -104,7 +104,7 @@ export const SecretMainPage = () => {
} }
}); });
// fetch folders // fetch folders
const { data: folders, isLoading: isFoldersLoading } = useGetProjectFolders({ const { data: folderData, isLoading: isFoldersLoading } = useGetProjectFolders({
projectId: workspaceId, projectId: workspaceId,
environment, environment,
path: secretPath path: secretPath
@@ -163,7 +163,9 @@ export const SecretMainPage = () => {
isPaused: !canDoReadRollback isPaused: !canDoReadRollback
}); });
const isNotEmtpy = Boolean(secrets?.length || folders?.length || secretImports?.length); const isNotEmtpy = Boolean(
secrets?.length || folderData?.folders?.length || secretImports?.length
);
const handleSortToggle = () => const handleSortToggle = () =>
setSortDir((state) => (state === SortDir.ASC ? SortDir.DESC : SortDir.ASC)); setSortDir((state) => (state === SortDir.ASC ? SortDir.DESC : SortDir.ASC));
@@ -292,7 +294,7 @@ export const SecretMainPage = () => {
/> />
)} )}
<FolderListView <FolderListView
folders={folders} folders={folderData?.folders}
environment={environment} environment={environment}
workspaceId={workspaceId} workspaceId={workspaceId}
secretPath={secretPath} secretPath={secretPath}
@@ -312,7 +314,7 @@ export const SecretMainPage = () => {
isProtectedBranch={isProtectedBranch} isProtectedBranch={isProtectedBranch}
/> />
)} )}
{!canReadSecret && folders?.length === 0 && <PermissionDeniedBanner />} {!canReadSecret && folderData?.folders?.length === 0 && <PermissionDeniedBanner />}
</div> </div>
</div> </div>
<CreateSecretForm <CreateSecretForm
@@ -352,7 +354,7 @@ export const SecretMainPage = () => {
workspaceId={workspaceId} workspaceId={workspaceId}
secretPath={secretPath} secretPath={secretPath}
secrets={secrets} secrets={secrets}
folders={folders} folders={folderData?.folders}
snapshotCount={snapshotCount} snapshotCount={snapshotCount}
onGoBack={handleResetSnapshot} onGoBack={handleResetSnapshot}
onClickListSnapshot={() => handlePopUpToggle("snapshots", true)} onClickListSnapshot={() => handlePopUpToggle("snapshots", true)}
@@ -125,11 +125,12 @@ export const SecretOverviewPage = () => {
secretPath, secretPath,
decryptFileKey: latestFileKey! decryptFileKey: latestFileKey!
}); });
const { folders, folderNames, isFolderPresentInEnv } = useGetFoldersByEnv({ const { folders, folderNames, isFolderPresentInEnv, isImportedFolderPresentInEnv } =
projectId: workspaceId, useGetFoldersByEnv({
path: secretPath, projectId: workspaceId,
environments: userAvailableEnvs.map(({ slug }) => slug) path: secretPath,
}); environments: userAvailableEnvs.map(({ slug }) => slug)
});
const { mutateAsync: createSecretV3 } = useCreateSecretV3(); const { mutateAsync: createSecretV3 } = useCreateSecretV3();
const { mutateAsync: updateSecretV3 } = useUpdateSecretV3(); const { mutateAsync: updateSecretV3 } = useUpdateSecretV3();
@@ -639,6 +640,7 @@ export const SecretOverviewPage = () => {
<SecretOverviewFolderRow <SecretOverviewFolderRow
folderName={folderName} folderName={folderName}
isFolderPresentInEnv={isFolderPresentInEnv} isFolderPresentInEnv={isFolderPresentInEnv}
isImportedFolderPresentInEnv={isImportedFolderPresentInEnv}
environments={visibleEnvs} environments={visibleEnvs}
key={`overview-${folderName}-${index + 1}`} key={`overview-${folderName}-${index + 1}`}
onClick={handleFolderClick} onClick={handleFolderClick}
@@ -1,13 +1,14 @@
import { faCheck, faFolder, faXmark } from "@fortawesome/free-solid-svg-icons"; import { faCheck, faFileImport, faFolder, faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { twMerge } from "tailwind-merge"; import { twMerge } from "tailwind-merge";
import { Td, Tr } from "@app/components/v2"; import { Td, Tooltip, Tr } from "@app/components/v2";
type Props = { type Props = {
folderName: string; folderName: string;
environments: { name: string; slug: string }[]; environments: { name: string; slug: string }[];
isFolderPresentInEnv: (name: string, env: string) => boolean; isFolderPresentInEnv: (name: string, env: string) => boolean;
isImportedFolderPresentInEnv: (name: string, env: string) => boolean;
onClick: (path: string) => void; onClick: (path: string) => void;
}; };
@@ -15,6 +16,7 @@ export const SecretOverviewFolderRow = ({
folderName, folderName,
environments = [], environments = [],
isFolderPresentInEnv, isFolderPresentInEnv,
isImportedFolderPresentInEnv,
onClick onClick
}: Props) => { }: Props) => {
return ( return (
@@ -29,17 +31,24 @@ export const SecretOverviewFolderRow = ({
</Td> </Td>
{environments.map(({ slug }, i) => { {environments.map(({ slug }, i) => {
const isPresent = isFolderPresentInEnv(folderName, slug); const isPresent = isFolderPresentInEnv(folderName, slug);
const isImportPresent = isImportedFolderPresentInEnv(folderName, slug);
return ( return (
<Td <Td
key={`sec-overview-${slug}-${i + 1}-folder`} key={`sec-overview-${slug}-${i + 1}-folder`}
className={twMerge( className={twMerge(
"border-r border-mineshaft-600 py-3 group-hover:bg-mineshaft-700", "border-r border-mineshaft-600 py-3 group-hover:bg-mineshaft-700",
isPresent ? "text-green-600" : "text-red-600" // eslint-disable-next-line no-nested-ternary
isPresent || isImportPresent ? "text-green-600" : "text-red-600"
)} )}
> >
<div className="flex justify-center"> <Tooltip isDisabled={!isImportPresent} content="Folder is imported">
<FontAwesomeIcon icon={isPresent ? faCheck : faXmark} /> <div className="flex justify-center">
</div> <FontAwesomeIcon
// eslint-disable-next-line no-nested-ternary
icon={isPresent ? faCheck : isImportPresent ? faFileImport : faXmark}
/>
</div>
</Tooltip>
</Td> </Td>
); );
})} })}