mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-09 22:28:25 +00:00
feat: added support for renaming folders in the overview page
This commit is contained in:
@@ -47,6 +47,7 @@ import {
|
|||||||
ProjectPermissionActions,
|
ProjectPermissionActions,
|
||||||
ProjectPermissionSub,
|
ProjectPermissionSub,
|
||||||
useOrganization,
|
useOrganization,
|
||||||
|
useProjectPermission,
|
||||||
useWorkspace
|
useWorkspace
|
||||||
} from "@app/context";
|
} from "@app/context";
|
||||||
import { usePopUp } from "@app/hooks";
|
import { usePopUp } from "@app/hooks";
|
||||||
@@ -59,8 +60,10 @@ import {
|
|||||||
useGetImportedSecretsAllEnvs,
|
useGetImportedSecretsAllEnvs,
|
||||||
useGetProjectSecretsAllEnv,
|
useGetProjectSecretsAllEnv,
|
||||||
useGetUserWsKey,
|
useGetUserWsKey,
|
||||||
|
useUpdateFolder,
|
||||||
useUpdateSecretV3
|
useUpdateSecretV3
|
||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
|
import { TSecretFolder } from "@app/hooks/api/types";
|
||||||
import { ProjectVersion } from "@app/hooks/api/workspace/types";
|
import { ProjectVersion } from "@app/hooks/api/workspace/types";
|
||||||
|
|
||||||
import { FolderForm } from "../SecretMainPage/components/ActionBar/FolderForm";
|
import { FolderForm } from "../SecretMainPage/components/ActionBar/FolderForm";
|
||||||
@@ -87,6 +90,7 @@ export const SecretOverviewPage = () => {
|
|||||||
const parentTableRef = useRef<HTMLTableElement>(null);
|
const parentTableRef = useRef<HTMLTableElement>(null);
|
||||||
const [expandableTableWidth, setExpandableTableWidth] = useState(0);
|
const [expandableTableWidth, setExpandableTableWidth] = useState(0);
|
||||||
const [sortDir, setSortDir] = useState<"asc" | "desc">("asc");
|
const [sortDir, setSortDir] = useState<"asc" | "desc">("asc");
|
||||||
|
const { permission } = useProjectPermission();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (parentTableRef.current) {
|
if (parentTableRef.current) {
|
||||||
@@ -201,11 +205,13 @@ export const SecretOverviewPage = () => {
|
|||||||
const { mutateAsync: updateSecretV3 } = useUpdateSecretV3();
|
const { mutateAsync: updateSecretV3 } = useUpdateSecretV3();
|
||||||
const { mutateAsync: deleteSecretV3 } = useDeleteSecretV3();
|
const { mutateAsync: deleteSecretV3 } = useDeleteSecretV3();
|
||||||
const { mutateAsync: createFolder } = useCreateFolder();
|
const { mutateAsync: createFolder } = useCreateFolder();
|
||||||
|
const { mutateAsync: updateFolder } = useUpdateFolder();
|
||||||
|
|
||||||
const { handlePopUpOpen, handlePopUpToggle, handlePopUpClose, popUp } = usePopUp([
|
const { handlePopUpOpen, handlePopUpToggle, handlePopUpClose, popUp } = usePopUp([
|
||||||
"addSecretsInAllEnvs",
|
"addSecretsInAllEnvs",
|
||||||
"addFolder",
|
"addFolder",
|
||||||
"misc"
|
"misc",
|
||||||
|
"updateFolder"
|
||||||
] as const);
|
] as const);
|
||||||
|
|
||||||
const handleFolderCreate = async (folderName: string) => {
|
const handleFolderCreate = async (folderName: string) => {
|
||||||
@@ -236,6 +242,64 @@ export const SecretOverviewPage = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleFolderUpdate = async (newFolderName: string) => {
|
||||||
|
const { name: oldFolderName } = popUp.updateFolder.data as TSecretFolder;
|
||||||
|
let processedUpdates = 0;
|
||||||
|
let totalFolderMatchCount = 0;
|
||||||
|
|
||||||
|
await Promise.allSettled(
|
||||||
|
userAvailableEnvs.map(async (env) => {
|
||||||
|
if (
|
||||||
|
permission.cannot(
|
||||||
|
ProjectPermissionActions.Edit,
|
||||||
|
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath })
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const folder = getFolderByNameAndEnv(oldFolderName, env.slug);
|
||||||
|
if (folder) {
|
||||||
|
totalFolderMatchCount += 1;
|
||||||
|
|
||||||
|
await updateFolder({
|
||||||
|
folderId: folder.id,
|
||||||
|
name: newFolderName,
|
||||||
|
path: secretPath,
|
||||||
|
environment: env.slug,
|
||||||
|
projectId: workspaceId
|
||||||
|
});
|
||||||
|
|
||||||
|
processedUpdates += 1;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
if (totalFolderMatchCount === 0) {
|
||||||
|
createNotification({
|
||||||
|
type: "info",
|
||||||
|
text: "You don't have access to rename selected folder"
|
||||||
|
});
|
||||||
|
} else if (processedUpdates === totalFolderMatchCount) {
|
||||||
|
createNotification({
|
||||||
|
type: "success",
|
||||||
|
text: "Successfully renamed folder across environments"
|
||||||
|
});
|
||||||
|
} else if (processedUpdates > 0) {
|
||||||
|
createNotification({
|
||||||
|
type: "warning",
|
||||||
|
text: "Partially renamed folder across environments"
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
createNotification({
|
||||||
|
type: "error",
|
||||||
|
text: "Failed to rename folder across environments"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePopUpClose("updateFolder");
|
||||||
|
};
|
||||||
|
|
||||||
const handleSecretCreate = async (env: string, key: string, value: string) => {
|
const handleSecretCreate = async (env: string, key: string, value: string) => {
|
||||||
try {
|
try {
|
||||||
// create folder if not existing
|
// create folder if not existing
|
||||||
@@ -726,6 +790,9 @@ export const SecretOverviewPage = () => {
|
|||||||
environments={visibleEnvs}
|
environments={visibleEnvs}
|
||||||
key={`overview-${folderName}-${index + 1}`}
|
key={`overview-${folderName}-${index + 1}`}
|
||||||
onClick={handleFolderClick}
|
onClick={handleFolderClick}
|
||||||
|
onToggleFolderEdit={(name: string) =>
|
||||||
|
handlePopUpOpen("updateFolder", { name })
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{!isTableLoading &&
|
{!isTableLoading &&
|
||||||
@@ -800,6 +867,18 @@ export const SecretOverviewPage = () => {
|
|||||||
<FolderForm onCreateFolder={handleFolderCreate} />
|
<FolderForm onCreateFolder={handleFolderCreate} />
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
<Modal
|
||||||
|
isOpen={popUp.updateFolder.isOpen}
|
||||||
|
onOpenChange={(isOpen) => handlePopUpToggle("updateFolder", isOpen)}
|
||||||
|
>
|
||||||
|
<ModalContent title="Edit Folder Name">
|
||||||
|
<FolderForm
|
||||||
|
isEdit
|
||||||
|
defaultFolderName={(popUp.updateFolder?.data as Pick<TSecretFolder, "name">)?.name}
|
||||||
|
onUpdateFolder={handleFolderUpdate}
|
||||||
|
/>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+16
-2
@@ -1,8 +1,8 @@
|
|||||||
import { faCheck, faFolder, faXmark } from "@fortawesome/free-solid-svg-icons";
|
import { faCheck, faFolder, faPencil, 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 { Checkbox, Td, Tr } from "@app/components/v2";
|
import { Checkbox, IconButton, Td, Tr } from "@app/components/v2";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
folderName: string;
|
folderName: string;
|
||||||
@@ -11,6 +11,7 @@ type Props = {
|
|||||||
onClick: (path: string) => void;
|
onClick: (path: string) => void;
|
||||||
isSelected: boolean;
|
isSelected: boolean;
|
||||||
onToggleFolderSelect: (folderName: string) => void;
|
onToggleFolderSelect: (folderName: string) => void;
|
||||||
|
onToggleFolderEdit: (name: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SecretOverviewFolderRow = ({
|
export const SecretOverviewFolderRow = ({
|
||||||
@@ -19,6 +20,7 @@ export const SecretOverviewFolderRow = ({
|
|||||||
isFolderPresentInEnv,
|
isFolderPresentInEnv,
|
||||||
isSelected,
|
isSelected,
|
||||||
onToggleFolderSelect,
|
onToggleFolderSelect,
|
||||||
|
onToggleFolderEdit,
|
||||||
onClick
|
onClick
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
return (
|
return (
|
||||||
@@ -43,6 +45,18 @@ export const SecretOverviewFolderRow = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>{folderName}</div>
|
<div>{folderName}</div>
|
||||||
|
<IconButton
|
||||||
|
ariaLabel="edit-folder"
|
||||||
|
variant="plain"
|
||||||
|
size="sm"
|
||||||
|
className="p-0 opacity-0 group-hover:opacity-100"
|
||||||
|
onClick={(e) => {
|
||||||
|
onToggleFolderEdit(folderName);
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faPencil} size="sm" />
|
||||||
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
</Td>
|
</Td>
|
||||||
{environments.map(({ slug }, i) => {
|
{environments.map(({ slug }, i) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user