feat: removed form in member and identity table, and added ellipsis for noting icons

This commit is contained in:
=
2024-10-25 00:59:40 +05:30
parent a1c2bc695c
commit f2b852a09e
9 changed files with 44 additions and 119 deletions
@@ -69,7 +69,7 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
projectMembershipId: req.body.projectMembershipId, projectMembershipId: req.body.projectMembershipId,
...req.body.type, ...req.body.type,
slug: req.body.slug || slugify(alphaNumericNanoId(8).toLowerCase()), slug: req.body.slug || slugify(alphaNumericNanoId(8)),
permissions: JSON.stringify(packRules(req.body.permissions)) permissions: JSON.stringify(packRules(req.body.permissions))
}); });
return { privilege }; return { privilege };
@@ -77,7 +77,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
projectId: req.body.projectId, projectId: req.body.projectId,
identityId: req.body.identityId, identityId: req.body.identityId,
...req.body.type, ...req.body.type,
slug: req.body.slug || slugify(alphaNumericNanoId(8).toLowerCase()), slug: req.body.slug || slugify(alphaNumericNanoId(8)),
permissions: JSON.stringify(packRules(req.body.permissions)) permissions: JSON.stringify(packRules(req.body.permissions))
}); });
return { privilege }; return { privilege };
@@ -18,6 +18,7 @@ import { createNotification } from "@app/components/notifications";
import { import {
Button, Button,
FormControl, FormControl,
FormLabel,
IconButton, IconButton,
Input, Input,
Modal, Modal,
@@ -133,9 +134,8 @@ export const IdentityProjectAdditionalPrivilegeModifySection = ({
const onSubmit = async (el: TFormSchema) => { const onSubmit = async (el: TFormSchema) => {
const accessType = !el.temporaryAccess.isTemporary const accessType = !el.temporaryAccess.isTemporary
? { role: el.slug, isTemporary: false as const } ? { isTemporary: false as const }
: { : {
role: el.slug,
isTemporary: true as const, isTemporary: true as const,
temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative, temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative,
temporaryRange: el.temporaryAccess.temporaryRange, temporaryRange: el.temporaryAccess.temporaryRange,
@@ -270,6 +270,7 @@ export const IdentityProjectAdditionalPrivilegeModifySection = ({
<Popover> <Popover>
<PopoverTrigger disabled={isIdentityEditDisabled} asChild> <PopoverTrigger disabled={isIdentityEditDisabled} asChild>
<div className="w-full max-w-md flex-grow"> <div className="w-full max-w-md flex-grow">
<FormLabel label="Access Type" />
<Tooltip content={toolTipText}> <Tooltip content={toolTipText}>
<Button <Button
variant="outline_bg" variant="outline_bg"
@@ -1,4 +1,4 @@
import { faFolder, faPlus, faTrash } from "@fortawesome/free-solid-svg-icons"; import { faEllipsisV, faFolder, faPlus, faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { format, formatDistance } from "date-fns"; import { format, formatDistance } from "date-fns";
import { AnimatePresence, motion } from "framer-motion"; import { AnimatePresence, motion } from "framer-motion";
@@ -189,7 +189,7 @@ export const IdentityProjectAdditionalPrivilegeSection = ({ identityMembershipDe
</Tooltip> </Tooltip>
</Td> </Td>
<Td> <Td>
<div className="opacity-0 transition-opacity duration-300 group-hover:opacity-100"> <div className="flex space-x-2 opacity-0 transition-opacity duration-300 group-hover:opacity-100">
<ProjectPermissionCan <ProjectPermissionCan
I={ProjectPermissionActions.Edit} I={ProjectPermissionActions.Edit}
a={ProjectPermissionSub.Identity} a={ProjectPermissionSub.Identity}
@@ -216,6 +216,9 @@ export const IdentityProjectAdditionalPrivilegeSection = ({ identityMembershipDe
</IconButton> </IconButton>
)} )}
</ProjectPermissionCan> </ProjectPermissionCan>
<IconButton ariaLabel="more-icon" variant="plain">
<FontAwesomeIcon icon={faEllipsisV} />
</IconButton>
</div> </div>
</Td> </Td>
</Tr> </Tr>
@@ -1,4 +1,4 @@
import { faFolder, faPlus, faTrash } from "@fortawesome/free-solid-svg-icons"; import { faEllipsisV, faFolder, faPlus, faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { format, formatDistance } from "date-fns"; import { format, formatDistance } from "date-fns";
import { AnimatePresence, motion } from "framer-motion"; import { AnimatePresence, motion } from "framer-motion";
@@ -196,7 +196,7 @@ export const MemberProjectAdditionalPrivilegeSection = ({ membershipDetails }: P
</Tooltip> </Tooltip>
</Td> </Td>
<Td> <Td>
<div className="opacity-0 transition-opacity duration-300 group-hover:opacity-100"> <div className="flex space-x-2 opacity-0 transition-opacity duration-300 group-hover:opacity-100">
<ProjectPermissionCan <ProjectPermissionCan
I={ProjectPermissionActions.Edit} I={ProjectPermissionActions.Edit}
a={ProjectPermissionSub.Member} a={ProjectPermissionSub.Member}
@@ -206,7 +206,7 @@ export const MemberProjectAdditionalPrivilegeSection = ({ membershipDetails }: P
{(isAllowed) => ( {(isAllowed) => (
<IconButton <IconButton
colorSchema="danger" colorSchema="danger"
ariaLabel="copy icon" ariaLabel="delete-icon"
variant="plain" variant="plain"
className="group relative" className="group relative"
isDisabled={!isAllowed || isOwnProjectMembershipDetails} isDisabled={!isAllowed || isOwnProjectMembershipDetails}
@@ -223,6 +223,13 @@ export const MemberProjectAdditionalPrivilegeSection = ({ membershipDetails }: P
</IconButton> </IconButton>
)} )}
</ProjectPermissionCan> </ProjectPermissionCan>
<IconButton
ariaLabel="more-icon"
variant="plain"
className="group relative"
>
<FontAwesomeIcon icon={faEllipsisV} />
</IconButton>
</div> </div>
</Td> </Td>
</Tr> </Tr>
@@ -18,6 +18,7 @@ import { createNotification } from "@app/components/notifications";
import { import {
Button, Button,
FormControl, FormControl,
FormLabel,
IconButton, IconButton,
Input, Input,
Modal, Modal,
@@ -93,6 +94,8 @@ export const MembershipProjectAdditionalPrivilegeModifySection = ({
const { data: privilegeDetails, isLoading } = useGetProjectUserPrivilegeDetails( const { data: privilegeDetails, isLoading } = useGetProjectUserPrivilegeDetails(
privilegeId || "" privilegeId || ""
); );
console.log(privilegeDetails);
const { permission } = useProjectPermission(); const { permission } = useProjectPermission();
const isMemberEditDisabled = permission.cannot( const isMemberEditDisabled = permission.cannot(
ProjectPermissionActions.Edit, ProjectPermissionActions.Edit,
@@ -131,9 +134,8 @@ export const MembershipProjectAdditionalPrivilegeModifySection = ({
const onSubmit = async (el: TFormSchema) => { const onSubmit = async (el: TFormSchema) => {
const accessType = !el.temporaryAccess.isTemporary const accessType = !el.temporaryAccess.isTemporary
? { role: el.slug, isTemporary: false as const } ? { isTemporary: false as const }
: { : {
role: el.slug,
isTemporary: true as const, isTemporary: true as const,
temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative, temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative,
temporaryRange: el.temporaryAccess.temporaryRange, temporaryRange: el.temporaryAccess.temporaryRange,
@@ -202,9 +204,7 @@ export const MembershipProjectAdditionalPrivilegeModifySection = ({
<IconButton ariaLabel="go-back" variant="plain" onClick={onGoBack}> <IconButton ariaLabel="go-back" variant="plain" onClick={onGoBack}>
<FontAwesomeIcon icon={faChevronLeft} /> <FontAwesomeIcon icon={faChevronLeft} />
</IconButton> </IconButton>
<h3 className="text-lg font-semibold text-mineshaft-100"> <h3 className="text-lg font-semibold text-mineshaft-100">Back</h3>
Modify Additional Privilege
</h3>
</div> </div>
<div className="flex items-center space-x-4"> <div className="flex items-center space-x-4">
{isDirty && ( {isDirty && (
@@ -268,6 +268,7 @@ export const MembershipProjectAdditionalPrivilegeModifySection = ({
<Popover> <Popover>
<PopoverTrigger disabled={isMemberEditDisabled} asChild> <PopoverTrigger disabled={isMemberEditDisabled} asChild>
<div className="w-full max-w-md flex-grow"> <div className="w-full max-w-md flex-grow">
<FormLabel label="Access Type" />
<Tooltip content={toolTipText}> <Tooltip content={toolTipText}>
<Button <Button
variant="outline_bg" variant="outline_bg"
@@ -5,7 +5,7 @@ import {
faArrowUp, faArrowUp,
faArrowUpRightFromSquare, faArrowUpRightFromSquare,
faClock, faClock,
faEdit, faEllipsisV,
faMagnifyingGlass, faMagnifyingGlass,
faPlus, faPlus,
faServer, faServer,
@@ -27,8 +27,6 @@ import {
HoverCardTrigger, HoverCardTrigger,
IconButton, IconButton,
Input, Input,
Modal,
ModalContent,
Pagination, Pagination,
Spinner, Spinner,
Table, Table,
@@ -47,13 +45,11 @@ import { withProjectPermission } from "@app/hoc";
import { usePagination, useResetPageHelper } from "@app/hooks"; import { usePagination, useResetPageHelper } from "@app/hooks";
import { useDeleteIdentityFromWorkspace, useGetWorkspaceIdentityMemberships } from "@app/hooks/api"; import { useDeleteIdentityFromWorkspace, useGetWorkspaceIdentityMemberships } from "@app/hooks/api";
import { OrderByDirection } from "@app/hooks/api/generic/types"; import { OrderByDirection } from "@app/hooks/api/generic/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 { ProjectIdentityOrderBy } from "@app/hooks/api/workspace/types"; import { ProjectIdentityOrderBy } from "@app/hooks/api/workspace/types";
import { usePopUp } from "@app/hooks/usePopUp"; import { usePopUp } from "@app/hooks/usePopUp";
import { IdentityModal } from "./components/IdentityModal"; import { IdentityModal } from "./components/IdentityModal";
import { IdentityRoleForm } from "./components/IdentityRoleForm";
const MAX_ROLES_TO_BE_SHOWN_IN_TABLE = 2; const MAX_ROLES_TO_BE_SHOWN_IN_TABLE = 2;
@@ -111,8 +107,7 @@ export const IdentityTab = withProjectPermission(
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
"identity", "identity",
"deleteIdentity", "deleteIdentity",
"upgradePlan", "upgradePlan"
"updateRole"
] as const); ] as const);
const onRemoveIdentitySubmit = async (identityId: string) => { const onRemoveIdentitySubmit = async (identityId: string) => {
@@ -234,7 +229,7 @@ export const IdentityTab = withProjectPermission(
{!isLoading && {!isLoading &&
data && data &&
data.identityMemberships.length > 0 && data.identityMemberships.length > 0 &&
data.identityMemberships.map((identityMember, index) => { data.identityMemberships.map((identityMember) => {
const { const {
identity: { id, name }, identity: { id, name },
roles, roles,
@@ -242,7 +237,7 @@ export const IdentityTab = withProjectPermission(
} = identityMember; } = identityMember;
return ( return (
<Tr <Tr
className="h-10 cursor-pointer transition-colors duration-100 hover:bg-mineshaft-700" className="group h-10 cursor-pointer transition-colors duration-100 hover:bg-mineshaft-700"
key={`st-v3-${id}`} key={`st-v3-${id}`}
role="button" role="button"
tabIndex={0} tabIndex={0}
@@ -348,24 +343,10 @@ export const IdentityTab = withProjectPermission(
</HoverCardContent> </HoverCardContent>
</HoverCard> </HoverCard>
)} )}
<Tooltip content="Edit permission">
<IconButton
size="sm"
variant="plain"
ariaLabel="update-role"
onClick={(evt) => {
evt.stopPropagation();
evt.preventDefault();
handlePopUpOpen("updateRole", { ...identityMember, index });
}}
>
<FontAwesomeIcon icon={faEdit} />
</IconButton>
</Tooltip>
</div> </div>
</Td> </Td>
<Td>{format(new Date(createdAt), "yyyy-MM-dd")}</Td> <Td>{format(new Date(createdAt), "yyyy-MM-dd")}</Td>
<Td className="flex justify-end"> <Td className="flex justify-end space-x-2 opacity-0 duration-300 group-hover:opacity-100">
<ProjectPermissionCan <ProjectPermissionCan
I={ProjectPermissionActions.Delete} I={ProjectPermissionActions.Delete}
a={ProjectPermissionSub.Identity} a={ProjectPermissionSub.Identity}
@@ -391,6 +372,9 @@ export const IdentityTab = withProjectPermission(
</IconButton> </IconButton>
)} )}
</ProjectPermissionCan> </ProjectPermissionCan>
<IconButton ariaLabel="more-icon" variant="plain">
<FontAwesomeIcon icon={faEllipsisV} />
</IconButton>
</Td> </Td>
</Tr> </Tr>
); );
@@ -417,31 +401,6 @@ export const IdentityTab = withProjectPermission(
/> />
)} )}
</TableContainer> </TableContainer>
<Modal
isOpen={popUp.updateRole.isOpen}
onOpenChange={(state) => handlePopUpToggle("updateRole", state)}
>
<ModalContent
className="max-w-3xl"
title={`Manage Access for ${
(popUp.updateRole.data as IdentityMembership)?.identity?.name
}`}
subTitle={`
Configure role-based access control by assigning machine identities a mix of roles and specific privileges. An identity will gain access to all actions within the roles assigned to it, not just the actions those roles share in common. You must choose at least one permanent role.
`}
>
<IdentityRoleForm
onOpenUpgradeModal={(description) =>
handlePopUpOpen("upgradePlan", { description })
}
identityProjectMember={
data?.identityMemberships[
(popUp.updateRole?.data as IdentityMembership & { index: number })?.index
] as IdentityMembership
}
/>
</ModalContent>
</Modal>
<IdentityModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} /> <IdentityModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
<DeleteActionModal <DeleteActionModal
isOpen={popUp.deleteIdentity.isOpen} isOpen={popUp.deleteIdentity.isOpen}
@@ -25,8 +25,7 @@ export const MembersSection = () => {
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([ const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
"addMember", "addMember",
"removeMember", "removeMember",
"upgradePlan", "upgradePlan"
"updateRole"
] as const); ] as const);
const handleRemoveUser = async () => { const handleRemoveUser = async () => {
@@ -72,11 +71,7 @@ export const MembersSection = () => {
)} )}
</ProjectPermissionCan> </ProjectPermissionCan>
</div> </div>
<MembersTable <MembersTable handlePopUpOpen={handlePopUpOpen} />
popUp={popUp}
handlePopUpOpen={handlePopUpOpen}
handlePopUpToggle={handlePopUpToggle}
/>
<AddMemberModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} /> <AddMemberModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
<DeleteActionModal <DeleteActionModal
isOpen={popUp.removeMember.isOpen} isOpen={popUp.removeMember.isOpen}
@@ -2,7 +2,7 @@ import { useMemo, useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { import {
faClock, faClock,
faEdit, faEllipsisV,
faMagnifyingGlass, faMagnifyingGlass,
faTrash, faTrash,
faUsers faUsers
@@ -18,8 +18,6 @@ import {
HoverCardTrigger, HoverCardTrigger,
IconButton, IconButton,
Input, Input,
Modal,
ModalContent,
Table, Table,
TableContainer, TableContainer,
TableSkeleton, TableSkeleton,
@@ -39,11 +37,8 @@ import {
} from "@app/context"; } from "@app/context";
import { useGetWorkspaceUsers } from "@app/hooks/api"; import { useGetWorkspaceUsers } from "@app/hooks/api";
import { ProjectMembershipRole } from "@app/hooks/api/roles/types"; import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
import { TWorkspaceUser } from "@app/hooks/api/types";
import { UsePopUpState } from "@app/hooks/usePopUp"; import { UsePopUpState } from "@app/hooks/usePopUp";
import { MemberRoleForm } from "./MemberRoleForm";
const MAX_ROLES_TO_BE_SHOWN_IN_TABLE = 2; const MAX_ROLES_TO_BE_SHOWN_IN_TABLE = 2;
const formatRoleName = (role: string, customRoleName?: string) => { const formatRoleName = (role: string, customRoleName?: string) => {
if (role === ProjectMembershipRole.Custom) return customRoleName; if (role === ProjectMembershipRole.Custom) return customRoleName;
@@ -53,15 +48,13 @@ const formatRoleName = (role: string, customRoleName?: string) => {
}; };
type Props = { type Props = {
popUp: UsePopUpState<["updateRole"]>;
handlePopUpOpen: ( handlePopUpOpen: (
popUpName: keyof UsePopUpState<["removeMember", "updateRole", "upgradePlan"]>, popUpName: keyof UsePopUpState<["removeMember", "upgradePlan"]>,
data?: {} data?: {}
) => void; ) => void;
handlePopUpToggle: (popUpName: keyof UsePopUpState<["updateRole"]>, state?: boolean) => void;
}; };
export const MembersTable = ({ popUp, handlePopUpOpen, handlePopUpToggle }: Props) => { export const MembersTable = ({ handlePopUpOpen }: Props) => {
const [searchMemberFilter, setSearchMemberFilter] = useState(""); const [searchMemberFilter, setSearchMemberFilter] = useState("");
const { currentWorkspace } = useWorkspace(); const { currentWorkspace } = useWorkspace();
@@ -107,7 +100,7 @@ export const MembersTable = ({ popUp, handlePopUpOpen, handlePopUpToggle }: Prop
<TBody> <TBody>
{isMembersLoading && <TableSkeleton columns={4} innerKey="project-members" />} {isMembersLoading && <TableSkeleton columns={4} innerKey="project-members" />}
{!isMembersLoading && {!isMembersLoading &&
filterdUsers?.map((projectMember, index) => { filterdUsers?.map((projectMember) => {
const { user: u, inviteEmail, id: membershipId, roles } = projectMember; const { user: u, inviteEmail, id: membershipId, roles } = projectMember;
const name = u.firstName || u.lastName ? `${u.firstName} ${u.lastName || ""}` : "-"; const name = u.firstName || u.lastName ? `${u.firstName} ${u.lastName || ""}` : "-";
const email = u?.email || inviteEmail; const email = u?.email || inviteEmail;
@@ -209,27 +202,11 @@ export const MembersTable = ({ popUp, handlePopUpOpen, handlePopUpToggle }: Prop
</HoverCardContent> </HoverCardContent>
</HoverCard> </HoverCard>
)} )}
{userId !== u?.id && (
<Tooltip content="Edit permission">
<IconButton
size="sm"
variant="plain"
ariaLabel="update-role"
onClick={(evt) => {
evt.preventDefault();
evt.stopPropagation();
handlePopUpOpen("updateRole", { ...projectMember, index });
}}
>
<FontAwesomeIcon icon={faEdit} />
</IconButton>
</Tooltip>
)}
</div> </div>
</Td> </Td>
<Td> <Td>
{userId !== u?.id && ( {userId !== u?.id && (
<div className="opacity-0 transition-opacity duration-300 group-hover:opacity-100"> <div className="flex items-center space-x-2 opacity-0 transition-opacity duration-300 group-hover:opacity-100">
<ProjectPermissionCan <ProjectPermissionCan
I={ProjectPermissionActions.Delete} I={ProjectPermissionActions.Delete}
a={ProjectPermissionSub.Member} a={ProjectPermissionSub.Member}
@@ -251,6 +228,9 @@ export const MembersTable = ({ popUp, handlePopUpOpen, handlePopUpToggle }: Prop
</IconButton> </IconButton>
)} )}
</ProjectPermissionCan> </ProjectPermissionCan>
<IconButton ariaLabel="more-icon" variant="plain">
<FontAwesomeIcon icon={faEllipsisV} />
</IconButton>
</div> </div>
)} )}
</Td> </Td>
@@ -263,27 +243,6 @@ export const MembersTable = ({ popUp, handlePopUpOpen, handlePopUpToggle }: Prop
<EmptyState title="No project members found" icon={faUsers} /> <EmptyState title="No project members found" icon={faUsers} />
)} )}
</TableContainer> </TableContainer>
<Modal
isOpen={popUp.updateRole.isOpen}
onOpenChange={(state) => handlePopUpToggle("updateRole", state)}
>
<ModalContent
className="max-w-4xl"
title={`Manage Access for ${(popUp.updateRole.data as TWorkspaceUser)?.user?.email}`}
subTitle={`
Configure role-based access control by assigning Infisical users a mix of roles and specific privileges. A user will gain access to all actions within the roles assigned to them, not just the actions those roles share in common. You must choose at least one permanent role.
`}
>
<MemberRoleForm
onOpenUpgradeModal={(description) => handlePopUpOpen("upgradePlan", { description })}
projectMember={
filterdUsers?.[
(popUp.updateRole?.data as TWorkspaceUser & { index: number })?.index
] as TWorkspaceUser
}
/>
</ModalContent>
</Modal>
</div> </div>
); );
}; };