mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 07:26:45 +00:00
Merge pull request #2099 from Infisical/identity-improvements
Identity Workflow Improvements (Table Menu Opts, Error Handling)
This commit is contained in:
+2
-2
@@ -4,7 +4,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { Button, FormControl, Modal, ModalContent,Select, SelectItem } from "@app/components/v2";
|
import { Button, FormControl, Modal, ModalContent, Select, SelectItem } from "@app/components/v2";
|
||||||
import { useWorkspace } from "@app/context";
|
import { useWorkspace } from "@app/context";
|
||||||
import {
|
import {
|
||||||
useAddIdentityToWorkspace,
|
useAddIdentityToWorkspace,
|
||||||
@@ -54,7 +54,7 @@ export const IdentityAddToProjectModal = ({ identityId, popUp, handlePopUpToggle
|
|||||||
const filteredWorkspaces = useMemo(() => {
|
const filteredWorkspaces = useMemo(() => {
|
||||||
const wsWorkspaceIds = new Map();
|
const wsWorkspaceIds = new Map();
|
||||||
|
|
||||||
projectMemberships?.forEach((projectMembership: any) => {
|
projectMemberships?.forEach((projectMembership) => {
|
||||||
wsWorkspaceIds.set(projectMembership.project.id, true);
|
wsWorkspaceIds.set(projectMembership.project.id, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+49
-22
@@ -1,9 +1,12 @@
|
|||||||
|
import { useMemo } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { faTrash } from "@fortawesome/free-solid-svg-icons";
|
import { faTrash } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
|
|
||||||
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { IconButton, Td, Tooltip, Tr } from "@app/components/v2";
|
import { IconButton, Td, Tooltip, Tr } from "@app/components/v2";
|
||||||
|
import { useWorkspace } from "@app/context";
|
||||||
import { IdentityMembership } from "@app/hooks/api/identities/types";
|
import { IdentityMembership } from "@app/hooks/api/identities/types";
|
||||||
import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
|
import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
@@ -21,7 +24,7 @@ const formatRoleName = (role: string, customRoleName?: string) => {
|
|||||||
if (role === ProjectMembershipRole.Admin) return "Admin";
|
if (role === ProjectMembershipRole.Admin) return "Admin";
|
||||||
if (role === ProjectMembershipRole.Member) return "Developer";
|
if (role === ProjectMembershipRole.Member) return "Developer";
|
||||||
if (role === ProjectMembershipRole.Viewer) return "Viewer";
|
if (role === ProjectMembershipRole.Viewer) return "Viewer";
|
||||||
if (role === ProjectMembershipRole.NoAccess) return "No access";
|
if (role === ProjectMembershipRole.NoAccess) return "No Access";
|
||||||
return role;
|
return role;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -29,12 +32,34 @@ export const IdentityProjectRow = ({
|
|||||||
membership: { id, createdAt, identity, project, roles },
|
membership: { id, createdAt, identity, project, roles },
|
||||||
handlePopUpOpen
|
handlePopUpOpen
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
|
const { workspaces } = useWorkspace();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
const isAccessible = useMemo(() => {
|
||||||
|
const workspaceIds = new Map();
|
||||||
|
|
||||||
|
workspaces?.forEach((workspace) => {
|
||||||
|
workspaceIds.set(workspace.id, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
return workspaceIds.has(project.id);
|
||||||
|
}, [workspaces, project]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tr
|
<Tr
|
||||||
className="group h-10 cursor-pointer transition-colors duration-300 hover:bg-mineshaft-700"
|
className="group h-10 cursor-pointer transition-colors duration-300 hover:bg-mineshaft-700"
|
||||||
key={`identity-project-membership-${id}`}
|
key={`identity-project-membership-${id}`}
|
||||||
onClick={() => router.push(`/project/${project.id}/members`)}
|
onClick={() => {
|
||||||
|
if (isAccessible) {
|
||||||
|
router.push(`/project/${project.id}/members`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
createNotification({
|
||||||
|
text: "Unable to access project",
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Td>{project.name}</Td>
|
<Td>{project.name}</Td>
|
||||||
<Td>{`${formatRoleName(roles[0].role, roles[0].customRoleName)}${
|
<Td>{`${formatRoleName(roles[0].role, roles[0].customRoleName)}${
|
||||||
@@ -42,26 +67,28 @@ export const IdentityProjectRow = ({
|
|||||||
}`}</Td>
|
}`}</Td>
|
||||||
<Td>{format(new Date(createdAt), "yyyy-MM-dd")}</Td>
|
<Td>{format(new Date(createdAt), "yyyy-MM-dd")}</Td>
|
||||||
<Td>
|
<Td>
|
||||||
<div className="opacity-0 transition-opacity duration-300 group-hover:opacity-100">
|
{isAccessible && (
|
||||||
<Tooltip content="Remove">
|
<div className="opacity-0 transition-opacity duration-300 group-hover:opacity-100">
|
||||||
<IconButton
|
<Tooltip content="Remove">
|
||||||
ariaLabel="copy icon"
|
<IconButton
|
||||||
variant="plain"
|
ariaLabel="copy icon"
|
||||||
className="group relative"
|
variant="plain"
|
||||||
onClick={(e) => {
|
className="group relative"
|
||||||
e.stopPropagation();
|
onClick={(e) => {
|
||||||
handlePopUpOpen("removeIdentityFromProject", {
|
e.stopPropagation();
|
||||||
identityId: identity.id,
|
handlePopUpOpen("removeIdentityFromProject", {
|
||||||
identityName: identity.name,
|
identityId: identity.id,
|
||||||
projectId: project.id,
|
identityName: identity.name,
|
||||||
projectName: project.name
|
projectId: project.id,
|
||||||
});
|
projectName: project.name
|
||||||
}}
|
});
|
||||||
>
|
}}
|
||||||
<FontAwesomeIcon icon={faTrash} />
|
>
|
||||||
</IconButton>
|
<FontAwesomeIcon icon={faTrash} />
|
||||||
</Tooltip>
|
</IconButton>
|
||||||
</div>
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</Td>
|
</Td>
|
||||||
</Tr>
|
</Tr>
|
||||||
);
|
);
|
||||||
|
|||||||
+1
-1
@@ -108,7 +108,7 @@ export const IdentitySection = withPermission(
|
|||||||
)}
|
)}
|
||||||
</OrgPermissionCan>
|
</OrgPermissionCan>
|
||||||
</div>
|
</div>
|
||||||
<IdentityTable />
|
<IdentityTable handlePopUpOpen={handlePopUpOpen} />
|
||||||
<IdentityModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
<IdentityModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
||||||
{/* <IdentityAuthMethodModal
|
{/* <IdentityAuthMethodModal
|
||||||
popUp={popUp}
|
popUp={popUp}
|
||||||
|
|||||||
+70
-16
@@ -1,13 +1,16 @@
|
|||||||
import Link from "next/link";
|
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { faEllipsis, faServer } from "@fortawesome/free-solid-svg-icons";
|
import { faEllipsis, faServer } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { OrgPermissionCan } from "@app/components/permissions";
|
import { OrgPermissionCan } from "@app/components/permissions";
|
||||||
import {
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
EmptyState,
|
EmptyState,
|
||||||
IconButton,
|
|
||||||
Select,
|
Select,
|
||||||
SelectItem,
|
SelectItem,
|
||||||
Table,
|
Table,
|
||||||
@@ -21,8 +24,19 @@ import {
|
|||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context";
|
import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context";
|
||||||
import { useGetIdentityMembershipOrgs, useGetOrgRoles, useUpdateIdentity } from "@app/hooks/api";
|
import { useGetIdentityMembershipOrgs, useGetOrgRoles, useUpdateIdentity } from "@app/hooks/api";
|
||||||
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
export const IdentityTable = () => {
|
type Props = {
|
||||||
|
handlePopUpOpen: (
|
||||||
|
popUpName: keyof UsePopUpState<["deleteIdentity"]>,
|
||||||
|
data?: {
|
||||||
|
identityId: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const IdentityTable = ({ handlePopUpOpen }: Props) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const orgId = currentOrg?.id || "";
|
const orgId = currentOrg?.id || "";
|
||||||
@@ -76,9 +90,7 @@ export const IdentityTable = () => {
|
|||||||
key={`identity-${id}`}
|
key={`identity-${id}`}
|
||||||
onClick={() => router.push(`/org/${orgId}/identities/${id}`)}
|
onClick={() => router.push(`/org/${orgId}/identities/${id}`)}
|
||||||
>
|
>
|
||||||
<Td>
|
<Td>{name}</Td>
|
||||||
<Link href={`/org/${orgId}/identities/${id}`}>{name}</Link>
|
|
||||||
</Td>
|
|
||||||
<Td>
|
<Td>
|
||||||
<OrgPermissionCan
|
<OrgPermissionCan
|
||||||
I={OrgPermissionActions.Edit}
|
I={OrgPermissionActions.Edit}
|
||||||
@@ -109,16 +121,58 @@ export const IdentityTable = () => {
|
|||||||
</OrgPermissionCan>
|
</OrgPermissionCan>
|
||||||
</Td>
|
</Td>
|
||||||
<Td>
|
<Td>
|
||||||
<div className="flex items-center justify-end space-x-4">
|
<DropdownMenu>
|
||||||
<IconButton
|
<DropdownMenuTrigger asChild className="rounded-lg">
|
||||||
ariaLabel="copy icon"
|
<div className="hover:text-primary-400 data-[state=open]:text-primary-400">
|
||||||
variant="plain"
|
<FontAwesomeIcon size="sm" icon={faEllipsis} />
|
||||||
className="group relative"
|
</div>
|
||||||
onClick={() => router.push(`/org/${orgId}/identities/${id}`)}
|
</DropdownMenuTrigger>
|
||||||
>
|
<DropdownMenuContent align="start" className="p-1">
|
||||||
<FontAwesomeIcon icon={faEllipsis} />
|
<OrgPermissionCan
|
||||||
</IconButton>
|
I={OrgPermissionActions.Edit}
|
||||||
</div>
|
a={OrgPermissionSubjects.Identity}
|
||||||
|
>
|
||||||
|
{(isAllowed) => (
|
||||||
|
<DropdownMenuItem
|
||||||
|
className={twMerge(
|
||||||
|
!isAllowed && "pointer-events-none cursor-not-allowed opacity-50"
|
||||||
|
)}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
router.push(`/org/${orgId}/identities/${id}`);
|
||||||
|
}}
|
||||||
|
disabled={!isAllowed}
|
||||||
|
>
|
||||||
|
Edit Identity
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
|
</OrgPermissionCan>
|
||||||
|
<OrgPermissionCan
|
||||||
|
I={OrgPermissionActions.Delete}
|
||||||
|
a={OrgPermissionSubjects.Identity}
|
||||||
|
>
|
||||||
|
{(isAllowed) => (
|
||||||
|
<DropdownMenuItem
|
||||||
|
className={twMerge(
|
||||||
|
isAllowed
|
||||||
|
? "hover:!bg-red-500 hover:!text-white"
|
||||||
|
: "pointer-events-none cursor-not-allowed opacity-50"
|
||||||
|
)}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handlePopUpOpen("deleteIdentity", {
|
||||||
|
identityId: id,
|
||||||
|
name
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
disabled={!isAllowed}
|
||||||
|
>
|
||||||
|
Delete Identity
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
|
</OrgPermissionCan>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
</Td>
|
</Td>
|
||||||
</Tr>
|
</Tr>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user