improvement: minor UI adjustments to make project access control tabs more uniform

This commit is contained in:
Scott Wilson
2025-06-16 17:17:55 -07:00
parent a89bd08c08
commit 0c5155f8e6
6 changed files with 137 additions and 100 deletions
@@ -72,7 +72,7 @@ export const GroupsSection = () => {
<ProjectPermissionCan I={ProjectPermissionActions.Create} a={ProjectPermissionSub.Groups}> <ProjectPermissionCan I={ProjectPermissionActions.Create} a={ProjectPermissionSub.Groups}>
{(isAllowed) => ( {(isAllowed) => (
<Button <Button
colorSchema="primary" colorSchema="secondary"
type="submit" type="submit"
leftIcon={<FontAwesomeIcon icon={faPlus} />} leftIcon={<FontAwesomeIcon icon={faPlus} />}
onClick={() => handleAddGroupModal()} onClick={() => handleAddGroupModal()}
@@ -2,10 +2,11 @@ import { useMemo } from "react";
import { import {
faArrowDown, faArrowDown,
faArrowUp, faArrowUp,
faEllipsisV,
faMagnifyingGlass, faMagnifyingGlass,
faSearch, faSearch,
faTrash, faUsers,
faUsers faUsersSlash
} from "@fortawesome/free-solid-svg-icons"; } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useNavigate } from "@tanstack/react-router"; import { useNavigate } from "@tanstack/react-router";
@@ -13,6 +14,10 @@ import { format } from "date-fns";
import { ProjectPermissionCan } from "@app/components/permissions"; import { ProjectPermissionCan } from "@app/components/permissions";
import { import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
EmptyState, EmptyState,
IconButton, IconButton,
Input, Input,
@@ -184,33 +189,42 @@ export const GroupTable = ({ handlePopUpOpen }: Props) => {
</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">
<ProjectPermissionCan <Tooltip className="max-w-sm text-center" content="Options">
I={ProjectPermissionActions.Delete} <DropdownMenu>
a={ProjectPermissionSub.Groups} <DropdownMenuTrigger asChild>
> <IconButton
{(isAllowed) => ( ariaLabel="Options"
<div className="opacity-0 transition-opacity duration-300 group-hover:opacity-100"> colorSchema="secondary"
<Tooltip content="Remove"> className="w-6"
<IconButton variant="plain"
onClick={(e) => { >
e.stopPropagation(); <FontAwesomeIcon icon={faEllipsisV} />
handlePopUpOpen("deleteGroup", { </IconButton>
id, </DropdownMenuTrigger>
name <DropdownMenuContent sideOffset={2} align="end">
}); <ProjectPermissionCan
}} I={ProjectPermissionActions.Delete}
colorSchema="danger" a={ProjectPermissionSub.Groups}
variant="plain" >
ariaLabel="update" {(isAllowed) => (
className="ml-4" <DropdownMenuItem
isDisabled={!isAllowed} icon={<FontAwesomeIcon icon={faUsersSlash} />}
> isDisabled={!isAllowed}
<FontAwesomeIcon icon={faTrash} /> onClick={(e) => {
</IconButton> e.stopPropagation();
</Tooltip> handlePopUpOpen("deleteGroup", {
</div> id,
)} name
</ProjectPermissionCan> });
}}
>
Remove Group From Project
</DropdownMenuItem>
)}
</ProjectPermissionCan>
</DropdownMenuContent>
</DropdownMenu>
</Tooltip>
</Td> </Td>
</Tr> </Tr>
); );
@@ -3,12 +3,13 @@ import {
faArrowDown, faArrowDown,
faArrowUp, faArrowUp,
faArrowUpRightFromSquare, faArrowUpRightFromSquare,
faBookOpen,
faCircleMinus,
faClock, faClock,
faEllipsisV, faEllipsisV,
faMagnifyingGlass, faMagnifyingGlass,
faPlus, faPlus,
faServer, faServer
faXmark
} from "@fortawesome/free-solid-svg-icons"; } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useNavigate } from "@tanstack/react-router"; import { useNavigate } from "@tanstack/react-router";
@@ -21,6 +22,10 @@ import { ProjectPermissionCan } from "@app/components/permissions";
import { import {
Button, Button,
DeleteActionModal, DeleteActionModal,
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
EmptyState, EmptyState,
HoverCard, HoverCard,
HoverCardContent, HoverCardContent,
@@ -163,20 +168,21 @@ export const IdentityTab = withProjectPermission(
> >
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4"> <div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<div className="mb-4 flex items-center justify-between"> <div className="mb-4 flex items-center justify-between">
<p className="text-xl font-semibold text-mineshaft-100">Identities</p> <div className="flex items-center gap-1">
<div className="flex w-full justify-end pr-4"> <p className="text-xl font-semibold text-mineshaft-100">Identities</p>
<a <a
href="https://infisical.com/docs/documentation/platform/identities/overview"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
href="https://infisical.com/docs/documentation/platform/identities/overview"
> >
<span className="flex w-max cursor-pointer items-center rounded-md border border-mineshaft-500 bg-mineshaft-600 px-4 py-2 text-mineshaft-200 duration-200 hover:border-primary/40 hover:bg-primary/10 hover:text-white"> <div className="ml-1 mt-[0.16rem] inline-block rounded-md bg-yellow/20 px-1.5 text-sm text-yellow opacity-80 hover:opacity-100">
Documentation{" "} <FontAwesomeIcon icon={faBookOpen} className="mr-1.5" />
<span>Docs</span>
<FontAwesomeIcon <FontAwesomeIcon
icon={faArrowUpRightFromSquare} icon={faArrowUpRightFromSquare}
className="mb-[0.06rem] ml-1 text-xs" className="mb-[0.07rem] ml-1.5 text-[10px]"
/> />
</span> </div>
</a> </a>
</div> </div>
<ProjectPermissionCan <ProjectPermissionCan
@@ -185,7 +191,7 @@ export const IdentityTab = withProjectPermission(
> >
{(isAllowed) => ( {(isAllowed) => (
<Button <Button
colorSchema="primary" colorSchema="secondary"
type="submit" type="submit"
leftIcon={<FontAwesomeIcon icon={faPlus} />} leftIcon={<FontAwesomeIcon icon={faPlus} />}
onClick={() => handlePopUpOpen("identity")} onClick={() => handlePopUpOpen("identity")}
@@ -231,7 +237,7 @@ export const IdentityTab = withProjectPermission(
</Th> </Th>
<Th className="w-1/3">Role</Th> <Th className="w-1/3">Role</Th>
<Th>Added on</Th> <Th>Added on</Th>
<Th className="w-16">{isFetching ? <Spinner size="xs" /> : null}</Th> <Th className="w-5">{isFetching ? <Spinner size="xs" /> : null}</Th>
</Tr> </Tr>
</THead> </THead>
<TBody> <TBody>
@@ -372,37 +378,46 @@ export const IdentityTab = withProjectPermission(
</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 space-x-2 opacity-0 duration-300 group-hover:opacity-100"> <Td className="flex justify-end space-x-2">
<ProjectPermissionCan <Tooltip className="max-w-sm text-center" content="Options">
I={ProjectPermissionActions.Delete} <DropdownMenu>
a={subject(ProjectPermissionSub.Identity, { <DropdownMenuTrigger asChild>
identityId: id <IconButton
})} ariaLabel="Options"
> colorSchema="secondary"
{(isAllowed) => ( className="w-6"
<IconButton variant="plain"
onClick={(evt) => { >
evt.stopPropagation(); <FontAwesomeIcon icon={faEllipsisV} />
evt.preventDefault(); </IconButton>
handlePopUpOpen("deleteIdentity", { </DropdownMenuTrigger>
identityId: id, <DropdownMenuContent sideOffset={2} align="end">
name <ProjectPermissionCan
}); I={ProjectPermissionActions.Delete}
}} a={subject(ProjectPermissionSub.Identity, {
size="lg" identityId: id
colorSchema="danger" })}
variant="plain" >
ariaLabel="update" {(isAllowed) => (
className="ml-4" <DropdownMenuItem
isDisabled={!isAllowed} icon={<FontAwesomeIcon icon={faCircleMinus} />}
> isDisabled={!isAllowed}
<FontAwesomeIcon icon={faXmark} /> onClick={(evt) => {
</IconButton> evt.stopPropagation();
)} evt.preventDefault();
</ProjectPermissionCan> handlePopUpOpen("deleteIdentity", {
<IconButton ariaLabel="more-icon" variant="plain"> identityId: id,
<FontAwesomeIcon icon={faEllipsisV} /> name
</IconButton> });
}}
>
Remove Identity From Project
</DropdownMenuItem>
)}
</ProjectPermissionCan>
</DropdownMenuContent>
</DropdownMenu>
</Tooltip>
</Td> </Td>
</Tr> </Tr>
); );
@@ -56,12 +56,12 @@ export const MembersSection = () => {
return ( return (
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4"> <div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<div className="mb-4 flex justify-between"> <div className="mb-4 flex items-center justify-between">
<p className="text-xl font-semibold text-mineshaft-100">Users</p> <p className="text-xl font-semibold text-mineshaft-100">Users</p>
<ProjectPermissionCan I={ProjectPermissionActions.Create} a={ProjectPermissionSub.Member}> <ProjectPermissionCan I={ProjectPermissionActions.Create} a={ProjectPermissionSub.Member}>
{(isAllowed) => ( {(isAllowed) => (
<Button <Button
colorSchema="primary" colorSchema="secondary"
type="submit" type="submit"
leftIcon={<FontAwesomeIcon icon={faPlus} />} leftIcon={<FontAwesomeIcon icon={faPlus} />}
onClick={() => handlePopUpOpen("addMember")} onClick={() => handlePopUpOpen("addMember")}
@@ -9,7 +9,7 @@ import {
faFilter, faFilter,
faMagnifyingGlass, faMagnifyingGlass,
faSearch, faSearch,
faTrash, faUserMinus,
faUsers faUsers
} from "@fortawesome/free-solid-svg-icons"; } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
@@ -416,32 +416,40 @@ export const MembersTable = ({ handlePopUpOpen }: Props) => {
</Td> </Td>
<Td> <Td>
{userId !== u?.id && ( {userId !== u?.id && (
<div className="flex items-center space-x-2 opacity-0 transition-opacity duration-300 group-hover:opacity-100"> <Tooltip className="max-w-sm text-center" content="Options">
<ProjectPermissionCan <DropdownMenu>
I={ProjectPermissionActions.Delete} <DropdownMenuTrigger asChild>
a={ProjectPermissionSub.Member}
>
{(isAllowed) => (
<IconButton <IconButton
colorSchema="danger" ariaLabel="Options"
colorSchema="secondary"
className="w-6"
variant="plain" variant="plain"
ariaLabel="update"
className="ml-4"
isDisabled={userId === u?.id || !isAllowed}
onClick={(evt) => {
evt.preventDefault();
evt.stopPropagation();
handlePopUpOpen("removeMember", { username: u.username });
}}
> >
<FontAwesomeIcon icon={faTrash} /> <FontAwesomeIcon icon={faEllipsisV} />
</IconButton> </IconButton>
)} </DropdownMenuTrigger>
</ProjectPermissionCan> <DropdownMenuContent sideOffset={2} align="end">
<IconButton ariaLabel="more-icon" variant="plain"> <ProjectPermissionCan
<FontAwesomeIcon icon={faEllipsisV} /> I={ProjectPermissionActions.Delete}
</IconButton> a={ProjectPermissionSub.Member}
</div> >
{(isAllowed) => (
<DropdownMenuItem
icon={<FontAwesomeIcon icon={faUserMinus} />}
isDisabled={!isAllowed}
onClick={(evt) => {
evt.preventDefault();
evt.stopPropagation();
handlePopUpOpen("removeMember", { username: u.username });
}}
>
Remove User From Project
</DropdownMenuItem>
)}
</ProjectPermissionCan>
</DropdownMenuContent>
</DropdownMenu>
</Tooltip>
)} )}
</Td> </Td>
</Tr> </Tr>
@@ -10,10 +10,10 @@ export const ProjectRoleListTab = withProjectPermission(
return ( return (
<motion.div <motion.div
key="role-list" key="role-list"
transition={{ duration: 0.1 }} transition={{ duration: 0.15 }}
initial={{ opacity: 0, translateX: -30 }} initial={{ opacity: 0, translateX: 30 }}
animate={{ opacity: 1, translateX: 0 }} animate={{ opacity: 1, translateX: 0 }}
exit={{ opacity: 0, translateX: -30 }} exit={{ opacity: 0, translateX: 30 }}
> >
<ProjectRoleList /> <ProjectRoleList />
</motion.div> </motion.div>