improvements: org and project policy page ui improvements

This commit is contained in:
Scott Wilson
2025-07-16 14:48:57 -07:00
parent 6ce6c276cd
commit 844f2bb72c
16 changed files with 187 additions and 223 deletions
@@ -1,95 +0,0 @@
import { faCheck, faCopy, faPencil } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { OrgPermissionCan } from "@app/components/permissions";
import { IconButton, Tooltip } from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context";
import { useTimedReset } from "@app/hooks";
import { useGetOrgRole } from "@app/hooks/api";
import { UsePopUpState } from "@app/hooks/usePopUp";
type Props = {
roleId: string;
handlePopUpOpen: (popUpName: keyof UsePopUpState<["role"]>, data?: object) => void;
};
export const RoleDetailsSection = ({ roleId, handlePopUpOpen }: Props) => {
const [copyTextId, isCopyingId, setCopyTextId] = useTimedReset<string>({
initialState: "Copy ID to clipboard"
});
const { currentOrg } = useOrganization();
const orgId = currentOrg?.id || "";
const { data } = useGetOrgRole(orgId, roleId);
const isCustomRole = !["admin", "member", "no-access"].includes(data?.slug ?? "");
return data ? (
<div className="rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<div className="flex items-center justify-between border-b border-mineshaft-400 pb-4">
<h3 className="text-lg font-semibold text-mineshaft-100">Org Role Details</h3>
{isCustomRole && (
<OrgPermissionCan I={OrgPermissionActions.Edit} a={OrgPermissionSubjects.Role}>
{(isAllowed) => {
return (
<Tooltip content="Edit Role">
<IconButton
isDisabled={!isAllowed}
ariaLabel="copy icon"
variant="plain"
className="group relative"
onClick={() =>
handlePopUpOpen("role", {
roleId
})
}
>
<FontAwesomeIcon icon={faPencil} />
</IconButton>
</Tooltip>
);
}}
</OrgPermissionCan>
)}
</div>
<div className="pt-4">
<div className="mb-4">
<p className="text-sm font-semibold text-mineshaft-300">Role ID</p>
<div className="group flex align-top">
<p className="text-sm text-mineshaft-300">{roleId}</p>
<div className="opacity-0 transition-opacity duration-300 group-hover:opacity-100">
<Tooltip content={copyTextId}>
<IconButton
ariaLabel="copy icon"
variant="plain"
className="group relative ml-2"
onClick={() => {
navigator.clipboard.writeText(roleId);
setCopyTextId("Copied");
}}
>
<FontAwesomeIcon icon={isCopyingId ? faCheck : faCopy} />
</IconButton>
</Tooltip>
</div>
</div>
</div>
<div className="mb-4">
<p className="text-sm font-semibold text-mineshaft-300">Name</p>
<p className="text-sm text-mineshaft-300">{data.name}</p>
</div>
<div className="mb-4">
<p className="text-sm font-semibold text-mineshaft-300">Slug</p>
<p className="text-sm text-mineshaft-300">{data.slug}</p>
</div>
<div className="mb-4">
<p className="text-sm font-semibold text-mineshaft-300">Description</p>
<p className="text-sm text-mineshaft-300">
{data.description?.length ? data.description : "-"}
</p>
</div>
</div>
</div>
) : (
<div />
);
};
@@ -76,17 +76,18 @@ export const OrgPermissionAdminConsoleRow = ({ isEditable, control, setValue }:
className="h-10 cursor-pointer transition-colors duration-100 hover:bg-mineshaft-700"
onClick={() => setIsRowExpanded.toggle()}
>
<Td>
<FontAwesomeIcon icon={isRowExpanded ? faChevronDown : faChevronRight} />
<Td className="w-4">
<FontAwesomeIcon className="w-4" icon={isRowExpanded ? faChevronDown : faChevronRight} />
</Td>
<Td>Organization Admin Console</Td>
<Td className="w-full select-none">Organization Admin Console</Td>
<Td>
<Select
value={selectedPermissionCategory}
className="w-40 bg-mineshaft-600"
dropdownContainerClassName="border border-mineshaft-600 bg-mineshaft-800"
className="h-8 w-40 bg-mineshaft-700"
dropdownContainerClassName="border text-left border-mineshaft-600 bg-mineshaft-800"
onValueChange={handlePermissionChange}
isDisabled={!isEditable}
position="popper"
>
<SelectItem value={Permission.NoAccess}>No Access</SelectItem>
<SelectItem value={Permission.Custom}>Custom</SelectItem>
@@ -97,9 +98,9 @@ export const OrgPermissionAdminConsoleRow = ({ isEditable, control, setValue }:
<Tr>
<Td
colSpan={3}
className={`bg-bunker-600 px-0 py-0 ${isRowExpanded && "border-mineshaft-500 p-8"}`}
className={`bg-mineshaft-900 px-0 py-0 ${isRowExpanded && "border-mineshaft-500 p-8"}`}
>
<div className="grid grid-cols-3 gap-4">
<div className="flex flex-grow flex-wrap justify-start gap-x-8 gap-y-4">
{PERMISSION_ACTIONS.map(({ action, label }) => {
return (
<Controller
@@ -124,17 +124,18 @@ export const OrgPermissionAppConnectionRow = ({ isEditable, control, setValue }:
className="h-10 cursor-pointer transition-colors duration-100 hover:bg-mineshaft-700"
onClick={() => setIsRowExpanded.toggle()}
>
<Td>
<FontAwesomeIcon icon={isRowExpanded ? faChevronDown : faChevronRight} />
<Td className="w-4">
<FontAwesomeIcon className="w-4" icon={isRowExpanded ? faChevronDown : faChevronRight} />
</Td>
<Td>App Connections</Td>
<Td className="w-full select-none">App Connections</Td>
<Td>
<Select
value={selectedPermissionCategory}
className="w-40 bg-mineshaft-600"
dropdownContainerClassName="border border-mineshaft-600 bg-mineshaft-800"
className="h-8 w-40 bg-mineshaft-700"
dropdownContainerClassName="border text-left border-mineshaft-600 bg-mineshaft-800"
onValueChange={handlePermissionChange}
isDisabled={!isEditable}
position="popper"
>
<SelectItem value={Permission.NoAccess}>No Access</SelectItem>
<SelectItem value={Permission.ReadOnly}>Read Only</SelectItem>
@@ -147,9 +148,9 @@ export const OrgPermissionAppConnectionRow = ({ isEditable, control, setValue }:
<Tr>
<Td
colSpan={3}
className={`bg-bunker-600 px-0 py-0 ${isRowExpanded && "border-mineshaft-500 p-8"}`}
className={`bg-mineshaft-900 px-0 py-0 ${isRowExpanded && "border-mineshaft-500 p-8"}`}
>
<div className="grid grid-cols-3 gap-4">
<div className="flex flex-grow flex-wrap justify-start gap-x-8 gap-y-4">
{PERMISSION_ACTIONS.map(({ action, label }) => {
return (
<Controller
@@ -112,17 +112,18 @@ export const OrgPermissionBillingRow = ({ isEditable, control, setValue }: Props
className="h-10 cursor-pointer transition-colors duration-100 hover:bg-mineshaft-700"
onClick={() => setIsRowExpanded.toggle()}
>
<Td>
<FontAwesomeIcon icon={isRowExpanded ? faChevronDown : faChevronRight} />
<Td className="w-4">
<FontAwesomeIcon className="w-4" icon={isRowExpanded ? faChevronDown : faChevronRight} />
</Td>
<Td>Billing</Td>
<Td className="w-full select-none">Billing</Td>
<Td>
<Select
value={selectedPermissionCategory}
className="w-40 bg-mineshaft-600"
dropdownContainerClassName="border border-mineshaft-600 bg-mineshaft-800"
className="h-8 w-40 bg-mineshaft-700"
dropdownContainerClassName="border text-left border-mineshaft-600 bg-mineshaft-800"
onValueChange={handlePermissionChange}
isDisabled={!isEditable}
position="popper"
>
<SelectItem value={Permission.NoAccess}>No Access</SelectItem>
<SelectItem value={Permission.ReadOnly}>Read Only</SelectItem>
@@ -135,9 +136,9 @@ export const OrgPermissionBillingRow = ({ isEditable, control, setValue }: Props
<Tr>
<Td
colSpan={3}
className={`bg-bunker-600 px-0 py-0 ${isRowExpanded && "border-mineshaft-500 p-8"}`}
className={`bg-mineshaft-900 px-0 py-0 ${isRowExpanded && "border-mineshaft-500 p-8"}`}
>
<div className="grid grid-cols-3 gap-4">
<div className="flex flex-grow flex-wrap justify-start gap-x-8 gap-y-4">
{PERMISSION_ACTIONS.map(({ action, label }) => {
return (
<Controller
@@ -121,17 +121,18 @@ export const OrgGatewayPermissionRow = ({ isEditable, control, setValue }: Props
className="h-10 cursor-pointer transition-colors duration-100 hover:bg-mineshaft-700"
onClick={() => setIsRowExpanded.toggle()}
>
<Td>
<FontAwesomeIcon icon={isRowExpanded ? faChevronDown : faChevronRight} />
<Td className="w-4">
<FontAwesomeIcon className="w-4" icon={isRowExpanded ? faChevronDown : faChevronRight} />
</Td>
<Td>Gateways</Td>
<Td className="w-full select-none">Gateways</Td>
<Td>
<Select
value={selectedPermissionCategory}
className="w-40 bg-mineshaft-600"
dropdownContainerClassName="border border-mineshaft-600 bg-mineshaft-800"
className="h-8 w-40 bg-mineshaft-700"
dropdownContainerClassName="border text-left border-mineshaft-600 bg-mineshaft-800"
onValueChange={handlePermissionChange}
isDisabled={!isEditable}
position="popper"
>
<SelectItem value={Permission.NoAccess}>No Access</SelectItem>
<SelectItem value={Permission.ReadOnly}>Read Only</SelectItem>
@@ -144,9 +145,9 @@ export const OrgGatewayPermissionRow = ({ isEditable, control, setValue }: Props
<Tr>
<Td
colSpan={3}
className={`bg-bunker-600 px-0 py-0 ${isRowExpanded && "border-mineshaft-500 p-8"}`}
className={`bg-mineshaft-900 px-0 py-0 ${isRowExpanded && "border-mineshaft-500 p-8"}`}
>
<div className="grid grid-cols-3 gap-4">
<div className="flex flex-grow flex-wrap justify-start gap-x-8 gap-y-4">
{PERMISSION_ACTIONS.map(({ action, label }) => {
return (
<Controller
@@ -145,17 +145,18 @@ export const OrgPermissionGroupRow = ({ isEditable, control, setValue }: Props)
className="h-10 cursor-pointer transition-colors duration-100 hover:bg-mineshaft-700"
onClick={() => setIsRowExpanded.toggle()}
>
<Td>
<FontAwesomeIcon icon={isRowExpanded ? faChevronDown : faChevronRight} />
<Td className="w-4">
<FontAwesomeIcon className="w-4" icon={isRowExpanded ? faChevronDown : faChevronRight} />
</Td>
<Td>Group Management</Td>
<Td className="w-full select-none">Group Management</Td>
<Td>
<Select
value={selectedPermissionCategory}
className="w-40 bg-mineshaft-600"
dropdownContainerClassName="border border-mineshaft-600 bg-mineshaft-800"
className="h-8 w-40 bg-mineshaft-700"
dropdownContainerClassName="border text-left border-mineshaft-600 bg-mineshaft-800"
onValueChange={handlePermissionChange}
isDisabled={!isEditable}
position="popper"
>
<SelectItem value={Permission.NoAccess}>No Access</SelectItem>
<SelectItem value={Permission.ReadOnly}>Read Only</SelectItem>
@@ -168,9 +169,9 @@ export const OrgPermissionGroupRow = ({ isEditable, control, setValue }: Props)
<Tr>
<Td
colSpan={3}
className={`bg-bunker-600 px-0 py-0 ${isRowExpanded && "border-mineshaft-500 p-8"}`}
className={`bg-mineshaft-900 px-0 py-0 ${isRowExpanded && "border-mineshaft-500 p-8"}`}
>
<div className="grid grid-cols-3 gap-4">
<div className="flex flex-grow flex-wrap justify-start gap-x-8 gap-y-4">
{PERMISSION_ACTIONS.map(({ action, label }) => {
return (
<Controller
@@ -155,17 +155,18 @@ export const OrgPermissionIdentityRow = ({ isEditable, control, setValue }: Prop
className="h-10 cursor-pointer transition-colors duration-100 hover:bg-mineshaft-700"
onClick={() => setIsRowExpanded.toggle()}
>
<Td>
<FontAwesomeIcon icon={isRowExpanded ? faChevronDown : faChevronRight} />
<Td className="w-4">
<FontAwesomeIcon className="w-4" icon={isRowExpanded ? faChevronDown : faChevronRight} />
</Td>
<Td>Machine Identity Management</Td>
<Td className="w-full select-none">Machine Identity Management</Td>
<Td>
<Select
value={selectedPermissionCategory}
className="w-40 bg-mineshaft-600"
dropdownContainerClassName="border border-mineshaft-600 bg-mineshaft-800"
className="h-8 w-40 bg-mineshaft-700"
dropdownContainerClassName="border text-left border-mineshaft-600 bg-mineshaft-800"
onValueChange={handlePermissionChange}
isDisabled={!isEditable}
position="popper"
>
<SelectItem value={Permission.NoAccess}>No Access</SelectItem>
<SelectItem value={Permission.ReadOnly}>Read Only</SelectItem>
@@ -178,9 +179,9 @@ export const OrgPermissionIdentityRow = ({ isEditable, control, setValue }: Prop
<Tr>
<Td
colSpan={3}
className={`bg-bunker-600 px-0 py-0 ${isRowExpanded && "border-mineshaft-500 p-8"}`}
className={`bg-mineshaft-900 px-0 py-0 ${isRowExpanded && "border-mineshaft-500 p-8"}`}
>
<div className="grid grid-cols-3 gap-4">
<div className="flex flex-grow flex-wrap justify-start gap-x-8 gap-y-4">
{PERMISSION_ACTIONS.map(({ action, label }) => {
return (
<Controller
@@ -73,17 +73,18 @@ export const OrgPermissionKmipRow = ({ isEditable, control, setValue }: Props) =
className="h-10 cursor-pointer transition-colors duration-100 hover:bg-mineshaft-700"
onClick={() => setIsRowExpanded.toggle()}
>
<Td>
<FontAwesomeIcon icon={isRowExpanded ? faChevronDown : faChevronRight} />
<Td className="w-4">
<FontAwesomeIcon className="w-4" icon={isRowExpanded ? faChevronDown : faChevronRight} />
</Td>
<Td>KMIP</Td>
<Td className="w-full select-none">KMIP</Td>
<Td>
<Select
value={selectedPermissionCategory}
className="w-40 bg-mineshaft-600"
dropdownContainerClassName="border border-mineshaft-600 bg-mineshaft-800"
className="h-8 w-40 bg-mineshaft-700"
dropdownContainerClassName="border text-left border-mineshaft-600 bg-mineshaft-800"
onValueChange={handlePermissionChange}
isDisabled={!isEditable}
position="popper"
>
<SelectItem value={Permission.NoAccess}>No Access</SelectItem>
<SelectItem value={Permission.Custom}>Custom</SelectItem>
@@ -94,9 +95,9 @@ export const OrgPermissionKmipRow = ({ isEditable, control, setValue }: Props) =
<Tr>
<Td
colSpan={3}
className={`bg-bunker-600 px-0 py-0 ${isRowExpanded && "border-mineshaft-500 p-8"}`}
className={`bg-mineshaft-900 px-0 py-0 ${isRowExpanded && "border-mineshaft-500 p-8"}`}
>
<div className="grid grid-cols-3 gap-4">
<div className="flex flex-grow flex-wrap justify-start gap-x-8 gap-y-4">
{PERMISSION_ACTIONS.map(({ action, label }) => {
return (
<Controller
@@ -70,17 +70,18 @@ export const OrgPermissionSecretShareRow = ({ isEditable, control, setValue }: P
className="h-10 cursor-pointer transition-colors duration-100 hover:bg-mineshaft-700"
onClick={() => setIsRowExpanded.toggle()}
>
<Td>
<FontAwesomeIcon icon={isRowExpanded ? faChevronDown : faChevronRight} />
<Td className="w-4">
<FontAwesomeIcon className="w-4" icon={isRowExpanded ? faChevronDown : faChevronRight} />
</Td>
<Td>Secret Share</Td>
<Td className="w-full select-none">Secret Share</Td>
<Td>
<Select
value={selectedPermissionCategory}
className="w-40 bg-mineshaft-600"
dropdownContainerClassName="border border-mineshaft-600 bg-mineshaft-800"
className="h-8 w-40 bg-mineshaft-700"
dropdownContainerClassName="border text-left border-mineshaft-600 bg-mineshaft-800"
onValueChange={handlePermissionChange}
isDisabled={!isEditable}
position="popper"
>
<SelectItem value={Permission.NoAccess}>No Access</SelectItem>
<SelectItem value={Permission.Custom}>Custom</SelectItem>
@@ -91,9 +92,9 @@ export const OrgPermissionSecretShareRow = ({ isEditable, control, setValue }: P
<Tr>
<Td
colSpan={3}
className={`bg-bunker-600 px-0 py-0 ${isRowExpanded && "border-mineshaft-500 p-8"}`}
className={`bg-mineshaft-900 px-0 py-0 ${isRowExpanded && "border-mineshaft-500 p-8"}`}
>
<div className="grid grid-cols-3 gap-4">
<div className="flex flex-grow flex-wrap justify-start gap-x-8 gap-y-4">
{PERMISSION_ACTIONS.map(({ action, label }) => {
return (
<Controller
@@ -70,17 +70,18 @@ export const OrgRoleWorkspaceRow = ({ isEditable, control, setValue }: Props) =>
className="h-10 cursor-pointer transition-colors duration-100 hover:bg-mineshaft-700"
onClick={() => setIsRowExpanded.toggle()}
>
<Td>
<FontAwesomeIcon icon={isRowExpanded ? faChevronDown : faChevronRight} />
<Td className="w-4">
<FontAwesomeIcon className="w-4" icon={isRowExpanded ? faChevronDown : faChevronRight} />
</Td>
<Td>Project</Td>
<Td className="w-full select-none">Project</Td>
<Td>
<Select
value={selectedPermissionCategory}
className="w-40 bg-mineshaft-600"
dropdownContainerClassName="border border-mineshaft-600 bg-mineshaft-800"
className="h-8 w-40 bg-mineshaft-700"
dropdownContainerClassName="border text-left border-mineshaft-600 bg-mineshaft-800"
onValueChange={handlePermissionChange}
isDisabled={!isEditable}
position="popper"
>
<SelectItem value={Permission.NoAccess}>No Access</SelectItem>
<SelectItem value={Permission.Custom}>Custom</SelectItem>
@@ -91,9 +92,9 @@ export const OrgRoleWorkspaceRow = ({ isEditable, control, setValue }: Props) =>
<Tr>
<Td
colSpan={3}
className={`bg-bunker-600 px-0 py-0 ${isRowExpanded && "border-mineshaft-500 p-8"}`}
className={`bg-mineshaft-900 px-0 py-0 ${isRowExpanded && "border-mineshaft-500 p-8"}`}
>
<div className="grid grid-cols-3 gap-4">
<div className="flex flex-grow flex-wrap justify-start gap-x-8 gap-y-4">
{PERMISSION_ACTIONS.map(({ action, label }) => {
return (
<Controller
@@ -158,17 +158,18 @@ export const RolePermissionRow = ({ isEditable, title, formName, control, setVal
className="h-10 cursor-pointer transition-colors duration-100 hover:bg-mineshaft-700"
onClick={() => setIsRowExpanded.toggle()}
>
<Td>
<FontAwesomeIcon icon={isRowExpanded ? faChevronDown : faChevronRight} />
<Td className="w-4">
<FontAwesomeIcon className="w-4" icon={isRowExpanded ? faChevronDown : faChevronRight} />
</Td>
<Td>{title}</Td>
<Td className="w-full select-none">{title}</Td>
<Td>
<Select
value={selectedPermissionCategory}
className="w-40 bg-mineshaft-600"
dropdownContainerClassName="border border-mineshaft-600 bg-mineshaft-800"
className="h-8 w-40 bg-mineshaft-700"
dropdownContainerClassName="border text-left border-mineshaft-600 bg-mineshaft-800"
onValueChange={handlePermissionChange}
isDisabled={!isEditable}
position="popper"
>
<SelectItem value={Permission.NoAccess}>No Access</SelectItem>
<SelectItem value={Permission.ReadOnly}>Read Only</SelectItem>
@@ -181,9 +182,9 @@ export const RolePermissionRow = ({ isEditable, title, formName, control, setVal
<Tr>
<Td
colSpan={3}
className={`bg-bunker-600 px-0 py-0 ${isRowExpanded && "border-mineshaft-500 p-8"}`}
className={`bg-mineshaft-900 px-0 py-0 ${isRowExpanded && "border-mineshaft-500 p-8"}`}
>
<div className="grid grid-cols-3 gap-4">
<div className="flex flex-grow flex-wrap justify-start gap-x-8 gap-y-4">
{getPermissionList(formName).map(({ action, label }) => {
return (
<Controller
@@ -1,8 +1,10 @@
import { useForm } from "react-hook-form";
import { faSave } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { zodResolver } from "@hookform/resolvers/zod";
import { createNotification } from "@app/components/notifications";
import { Button, Table, TableContainer, TBody, Th, THead, Tr } from "@app/components/v2";
import { Button, Table, TableContainer, TBody } from "@app/components/v2";
import { OrgPermissionSubjects, useOrganization } from "@app/context";
import { useGetOrgRole, useUpdateOrgRole } from "@app/hooks/api";
import { OrgPermissionAppConnectionRow } from "@app/pages/organization/RoleByIDPage/components/RolePermissionsSection/OrgPermissionAppConnectionRow";
@@ -117,39 +119,39 @@ export const RolePermissionsSection = ({ roleId }: Props) => {
className="w-full rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4"
>
<div className="flex items-center justify-between border-b border-mineshaft-400 pb-4">
<h3 className="text-lg font-semibold text-mineshaft-100">Permissions</h3>
<div>
<h3 className="text-lg font-semibold text-mineshaft-100">Policies</h3>
<p className="text-sm leading-3 text-mineshaft-400">Configure granular access policies</p>
</div>
{isCustomRole && (
<div className="flex items-center">
{isDirty && (
<Button
className="mr-4 text-mineshaft-300"
variant="link"
isDisabled={isSubmitting || !isDirty}
isLoading={isSubmitting}
onClick={() => reset()}
>
Discard
</Button>
)}
<Button
colorSchema="primary"
colorSchema="secondary"
type="submit"
className="h-10 border"
leftIcon={<FontAwesomeIcon icon={faSave} />}
isDisabled={isSubmitting || !isDirty}
isLoading={isSubmitting}
>
Save
</Button>
<Button
className="ml-4 text-mineshaft-300"
variant="link"
isDisabled={isSubmitting || !isDirty}
isLoading={isSubmitting}
onClick={() => reset()}
>
Cancel
</Button>
</div>
)}
</div>
<div className="py-4">
<TableContainer>
<Table>
<THead>
<Tr>
<Th className="w-5" />
<Th>Resource</Th>
<Th>Permission</Th>
</Tr>
</THead>
<TBody>
{SIMPLE_PERMISSION_OPTIONS.map((permission) => {
return (