feat(frontend): send bypass reason on bypassed merges

This commit is contained in:
Alfonso Hernandez
2024-07-19 21:33:41 +02:00
parent bff8f55ea2
commit 22df51ab8e
3 changed files with 35 additions and 9 deletions
@@ -46,8 +46,10 @@ export const usePerformSecretApprovalRequestMerge = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation<{}, {}, TPerformSecretApprovalRequestMerge>({ return useMutation<{}, {}, TPerformSecretApprovalRequestMerge>({
mutationFn: async ({ id }) => { mutationFn: async ({ id, bypassReason }) => {
const { data } = await apiRequest.post(`/api/v1/secret-approval-requests/${id}/merge`); const { data } = await apiRequest.post(`/api/v1/secret-approval-requests/${id}/merge`, {
bypassReason
});
return data; return data;
}, },
onSuccess: (_, { id, workspaceId }) => { onSuccess: (_, { id, workspaceId }) => {
@@ -133,4 +133,5 @@ export type TUpdateSecretApprovalRequestStatusDTO = {
export type TPerformSecretApprovalRequestMerge = { export type TPerformSecretApprovalRequestMerge = {
id: string; id: string;
workspaceId: string; workspaceId: string;
bypassReason?: string;
}; };
@@ -6,12 +6,13 @@ import {
faLockOpen, faLockOpen,
faSquareCheck, faSquareCheck,
faSquareXmark, faSquareXmark,
faTriangleExclamation,
faUserLock} from "@fortawesome/free-solid-svg-icons"; faUserLock} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { twMerge } from "tailwind-merge"; import { twMerge } from "tailwind-merge";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import { Button, Checkbox } from "@app/components/v2"; import { Button, Checkbox, FormControl, Input } from "@app/components/v2";
import { import {
usePerformSecretApprovalRequestMerge, usePerformSecretApprovalRequestMerge,
useUpdateSecretApprovalRequestStatus useUpdateSecretApprovalRequestStatus
@@ -48,12 +49,19 @@ export const SecretApprovalRequestAction = ({
useUpdateSecretApprovalRequestStatus(); useUpdateSecretApprovalRequestStatus();
const [byPassApproval, setByPassApproval] = useState(false); const [byPassApproval, setByPassApproval] = useState(false);
const [bypassReason, setBypassReason] = useState("");
const isValidBypassReason = (value: string) => {
const trimmedValue = value.trim();
return trimmedValue.length >= 10;
};
const handleSecretApprovalRequestMerge = async () => { const handleSecretApprovalRequestMerge = async () => {
try { try {
await performSecretApprovalMerge({ await performSecretApprovalMerge({
id: approvalRequestId, id: approvalRequestId,
workspaceId workspaceId,
bypassReason: byPassApproval ? bypassReason : undefined
}); });
createNotification({ createNotification({
type: "success", type: "success",
@@ -92,7 +100,7 @@ export const SecretApprovalRequestAction = ({
if (!hasMerged && status === "open") { if (!hasMerged && status === "open") {
return ( return (
<div className="flex w-full items-center justify-between"> <div className="flex w-full items-start justify-between transition-all">
<div className="flex items-start space-x-4"> <div className="flex items-start space-x-4">
<FontAwesomeIcon <FontAwesomeIcon
icon={isMergable ? faSquareCheck : faSquareXmark} icon={isMergable ? faSquareCheck : faSquareXmark}
@@ -105,18 +113,33 @@ export const SecretApprovalRequestAction = ({
{Boolean(statusChangeByEmail) && `. Reopened by ${statusChangeByEmail}`} {Boolean(statusChangeByEmail) && `. Reopened by ${statusChangeByEmail}`}
</span> </span>
{!canApprove && isSoftEnforcement && ( {!canApprove && isSoftEnforcement && (
<div className="mt-1"> <div className="mt-2 flex flex-col space-y-2">
<Checkbox <Checkbox
onCheckedChange={(checked) => setByPassApproval(checked === true)} onCheckedChange={(checked) => setByPassApproval(checked === true)}
isChecked={byPassApproval} isChecked={byPassApproval}
id="byPassApproval" id="byPassApproval"
checkIndicatorBg="text-white" checkIndicatorBg="text-white"
className={byPassApproval ? "bg-red hover:bg-red-600 border-red" : ""} className={twMerge("mr-2", byPassApproval ? "bg-red hover:bg-red-600 border-red" : "")}
> >
<span className="text-red text-sm"> <span className="text-red text-xs">
Merge without waiting for approval (bypass secret change policy) Merge without waiting for approval (bypass secret change policy)
</span> </span>
</Checkbox> </Checkbox>
{byPassApproval && (
<FormControl
label="Reason for Bypass"
className="mt-2"
isRequired
tooltipText="Enter a reason for bypassing the secret change policy"
>
<Input
value={bypassReason}
onChange={(e) => setBypassReason(e.target.value)}
placeholder="Enter reason for bypass (min 10 chars)"
leftIcon={<FontAwesomeIcon icon={faTriangleExclamation} />}
/>
</FormControl>
)}
</div> </div>
)} )}
</span> </span>
@@ -137,7 +160,7 @@ export const SecretApprovalRequestAction = ({
leftIcon={<FontAwesomeIcon icon={!canApprove ? faLandMineOn : faCheck} />} leftIcon={<FontAwesomeIcon icon={!canApprove ? faLandMineOn : faCheck} />}
isDisabled={ isDisabled={
(!isMergable && canApprove) (!isMergable && canApprove)
|| (!canApprove && isSoftEnforcement && !byPassApproval) || (!canApprove && isSoftEnforcement && (!byPassApproval || !isValidBypassReason(bypassReason)))
} }
isLoading={isMerging} isLoading={isMerging}
onClick={handleSecretApprovalRequestMerge} onClick={handleSecretApprovalRequestMerge}