From 953cc3a850d77d01858c55d33321bf3b46434ec0 Mon Sep 17 00:00:00 2001 From: Scott Wilson Date: Fri, 27 Jun 2025 09:30:11 -0700 Subject: [PATCH] improvements: revise approval sequence table display and access request modal --- .../GenericFieldLabel/GenericFieldLabel.tsx | 11 +- .../AccessApprovalRequest.tsx | 4 +- .../components/ReviewAccessModal.tsx | 385 ++++++++++-------- .../components/ApprovalPolicyRow.tsx | 59 ++- 4 files changed, 256 insertions(+), 203 deletions(-) diff --git a/frontend/src/components/v2/GenericFieldLabel/GenericFieldLabel.tsx b/frontend/src/components/v2/GenericFieldLabel/GenericFieldLabel.tsx index 95eaf5745..b746a32d7 100644 --- a/frontend/src/components/v2/GenericFieldLabel/GenericFieldLabel.tsx +++ b/frontend/src/components/v2/GenericFieldLabel/GenericFieldLabel.tsx @@ -1,4 +1,6 @@ import { ReactNode } from "react"; +import { IconDefinition } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { twMerge } from "tailwind-merge"; type Props = { @@ -7,6 +9,7 @@ type Props = { className?: string; labelClassName?: string; truncate?: boolean; + icon?: IconDefinition; }; export const GenericFieldLabel = ({ @@ -14,11 +17,15 @@ export const GenericFieldLabel = ({ children, className, labelClassName, - truncate + truncate, + icon }: Props) => { return (
-

{label}

+
+ {icon && } +

{label}

+
{children ? (

{children}

) : ( diff --git a/frontend/src/pages/secret-manager/SecretApprovalsPage/components/AccessApprovalRequest/AccessApprovalRequest.tsx b/frontend/src/pages/secret-manager/SecretApprovalsPage/components/AccessApprovalRequest/AccessApprovalRequest.tsx index f8b935e29..a8cb199d4 100644 --- a/frontend/src/pages/secret-manager/SecretApprovalsPage/components/AccessApprovalRequest/AccessApprovalRequest.tsx +++ b/frontend/src/pages/secret-manager/SecretApprovalsPage/components/AccessApprovalRequest/AccessApprovalRequest.tsx @@ -247,9 +247,7 @@ export const AccessApprovalRequest = ({ }; else if (userReviewStatus === ApprovalStatus.APPROVED) { displayData = { - label: `Pending ${request.policy.approvals - request.reviewers.length} review${ - request.policy.approvals - request.reviewers.length > 1 ? "s" : "" - }`, + label: "Pending Additional Reviews", type: "primary", icon: faClipboardCheck }; diff --git a/frontend/src/pages/secret-manager/SecretApprovalsPage/components/AccessApprovalRequest/components/ReviewAccessModal.tsx b/frontend/src/pages/secret-manager/SecretApprovalsPage/components/AccessApprovalRequest/components/ReviewAccessModal.tsx index 4cea61cbe..ee0f18ee0 100644 --- a/frontend/src/pages/secret-manager/SecretApprovalsPage/components/AccessApprovalRequest/components/ReviewAccessModal.tsx +++ b/frontend/src/pages/secret-manager/SecretApprovalsPage/components/AccessApprovalRequest/components/ReviewAccessModal.tsx @@ -1,10 +1,9 @@ -import { useCallback, useMemo, useState } from "react"; +import { ReactNode, useCallback, useMemo, useState } from "react"; import { - faCheckCircle, - faCircle, - faTriangleExclamation, - faUsers, - faXmarkCircle + faBan, + faCheck, + faHourglass, + faTriangleExclamation } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import ms from "ms"; @@ -15,12 +14,10 @@ import { Button, Checkbox, FormControl, + GenericFieldLabel, Input, Modal, ModalContent, - Popover, - PopoverContent, - PopoverTrigger, Tooltip } from "@app/components/v2"; import { Badge } from "@app/components/v2/Badge"; @@ -38,10 +35,22 @@ import { groupBy } from "@app/lib/fn/array"; const getReviewedStatusSymbol = (status?: ApprovalStatus) => { if (status === ApprovalStatus.APPROVED) - return ; + return ( + + + + ); if (status === ApprovalStatus.REJECTED) - return ; - return ; + return ( + + + + ); + return ( + + + + ); }; export const ReviewAccessRequestModal = ({ @@ -267,139 +276,160 @@ export const ReviewAccessRequestModal = ({
-
-
-
Environment
-
{accessDetails.env || "-"}
-
-
-
Secret Path
-
{accessDetails.secretPath || "-"}
-
-
-
Access Type
-
{getAccessLabel()}
-
-
-
Permission
-
{requestedAccess}
-
-
-
Note
-
{request.note || "-"}
-
+
+ {accessDetails.env} + + {accessDetails.secretPath} + + {getAccessLabel()} + {requestedAccess} + {request.note && ( + + {request.note} + + )}
-
Approvers
-
- {approverSequence?.approvers?.map((approver, index) => ( -
-
-
- {index + 1} -
- {index !== (approverSequence?.approvers?.length || 0) - 1 && ( -
- )} - {index !== 0 && ( -
- )} -
-
-
-
Users
-
- {approver?.user - ?.map( - (el) => approverSequence?.membersGroupById?.[el.id]?.[0]?.user?.username - ) - .join(",") || "-"} -
-
-
-
Groups
-
- {approver?.group - ?.map( - (el) => - approverSequence?.projectGroupsGroupById?.[el.id]?.[0]?.group?.name - ) - .join(",") || "-"} -
-
-
-
-
Approvals Required
-
{approver.approvals || "-"}
-
-
- - - - - -
-
Reviewers
-
- {approver.reviewers.map((el, idx) => ( -
-
{el.username}
- - {getReviewedStatusSymbol(el?.status as ApprovalStatus)} - -
- ))} -
-
-
-
-
-
-
-
- ))} + +
+ Approvers + {approverSequence.isMyReviewInThisSequence && + request.status === ApprovalStatus.PENDING && ( + + Awaiting Your Review + + )} +
+
+ {approverSequence?.approvers && + approverSequence.approvers.map((approver, index) => { + const isInactive = + approverSequence?.currentSequence < + (approver.sequence ?? approverSequence.approvers!.length); + + const isPending = approverSequence?.currentSequence === approver.sequence; + + let StepComponent: ReactNode; + let BadgeComponent: ReactNode = null; + if (approver.hasRejected) { + StepComponent = ( + + + + ); + BadgeComponent = Rejected; + } else if (approver.hasApproved) { + StepComponent = ( + + + + ); + BadgeComponent = Approved; + } else if (isPending) { + StepComponent = ( + + + + ); + BadgeComponent = Pending; + } else { + StepComponent = ( + + {index + 1} + + ); + } + + return ( +
+ {approverSequence.approvers!.length > 1 && ( +
+
+ {StepComponent} +
+
+ )} +
+ + {approver?.user + ?.map( + (el) => approverSequence?.membersGroupById?.[el.id]?.[0]?.user?.username + ) + .join(", ")} + + + {approver?.group + ?.map( + (el) => + approverSequence?.projectGroupsGroupById?.[el.id]?.[0]?.group?.name + ) + .join(", ")} + + +
+ {approver.approvals} + {BadgeComponent && ( + +
Reviewers
+
+ {approver.reviewers.map((el, idx) => ( +
+
{el.username}
+ {getReviewedStatusSymbol(el?.status as ApprovalStatus)} +
+ ))} +
+
+ } + > +
{BadgeComponent}
+ + )} +
+ +
+
+ ); + })}
- {approverSequence.isMyReviewInThisSequence && - request.status === ApprovalStatus.PENDING && ( -
- Awaiting review from you. -
- )} {shouldBlockRequestActions ? (
) : ( <> -
- - -
{isSoftEnforcement && request.isRequestedByCurrentUser && !(request.isApprover && request.isSelfApproveAllowed) && @@ -453,11 +448,7 @@ export const ReviewAccessRequestModal = ({ onCheckedChange={(checked) => setBypassApproval(checked === true)} isChecked={bypassApproval} id="byPassApproval" - checkIndicatorBg="text-white" - className={twMerge( - "mr-2", - bypassApproval ? "border-red bg-red hover:bg-red-600" : "" - )} + className={twMerge("mr-2", bypassApproval ? "border-red/30 bg-red/10" : "")} > Approve without waiting for requirements to be met (bypass policy @@ -481,6 +472,42 @@ export const ReviewAccessRequestModal = ({ )}
)} +
+ + +
)}
diff --git a/frontend/src/pages/secret-manager/SecretApprovalsPage/components/ApprovalPolicyList/components/ApprovalPolicyRow.tsx b/frontend/src/pages/secret-manager/SecretApprovalsPage/components/ApprovalPolicyList/components/ApprovalPolicyRow.tsx index 0a674f5b4..408d718fa 100644 --- a/frontend/src/pages/secret-manager/SecretApprovalsPage/components/ApprovalPolicyList/components/ApprovalPolicyRow.tsx +++ b/frontend/src/pages/secret-manager/SecretApprovalsPage/components/ApprovalPolicyList/components/ApprovalPolicyRow.tsx @@ -1,5 +1,12 @@ import { useMemo } from "react"; -import { faEdit, faEllipsisV, faTrash } from "@fortawesome/free-solid-svg-icons"; +import { + faClipboardCheck, + faEdit, + faEllipsisV, + faTrash, + faUser, + faUserGroup +} from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { twMerge } from "tailwind-merge"; @@ -179,26 +186,40 @@ export const ApprovalPolicyRow = ({ }`} >
-
Approvers
+
Approvers
{labels?.map((el, index) => ( -
-
-
{index + 1}
-
- {index !== labels.length - 1 && ( -
+
+ {labels.length > 1 && ( +
+
+ {labels.length > 1 && ( + + {index + 1} + + )} +
+
)} - {index !== 0 && ( -
- )} - -
- {el.userLabels} - {el.groupLabels} - {el.approvals} +
+ + {el.userLabels} + + + {el.groupLabels} + + + {el.approvals} +
))}