diff --git a/frontend/src/views/SecretApprovalPage/components/AccessApprovalRequest/components/AccessPolicyForm.tsx b/frontend/src/views/SecretApprovalPage/components/AccessApprovalRequest/components/AccessPolicyForm.tsx new file mode 100644 index 000000000..9cf756c14 --- /dev/null +++ b/frontend/src/views/SecretApprovalPage/components/AccessApprovalRequest/components/AccessPolicyForm.tsx @@ -0,0 +1,261 @@ +import { useEffect } from "react"; +import { Controller, useForm } from "react-hook-form"; +import { faCheckCircle } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { z } from "zod"; + +import { createNotification } from "@app/components/notifications"; +import { + Button, + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuTrigger, + FormControl, + Input, + Modal, + ModalContent, + Select, + SelectItem +} from "@app/components/v2"; +import { useWorkspace } from "@app/context"; +import { + useCreateAccessApprovalPolicy, + useUpdateAccessApprovalPolicy +} from "@app/hooks/api/accessApproval"; +import { TAccessApprovalPolicy } from "@app/hooks/api/accessApproval/types"; +import { TWorkspaceUser } from "@app/hooks/api/users/types"; + +type Props = { + isOpen?: boolean; + onToggle: (isOpen: boolean) => void; + members?: TWorkspaceUser[]; + projectSlug: string; + editValues?: TAccessApprovalPolicy; +}; + +const formSchema = z + .object({ + environment: z.string(), + name: z.string().optional(), + secretPath: z.string().optional(), + approvals: z.number().min(1), + approvers: z.string().array().min(1) + }) + .refine((data) => data.approvals <= data.approvers.length, { + path: ["approvals"], + message: "The number of approvals should be lower than the number of approvers." + }); + +type TFormSchema = z.infer; + +export const AccessPolicyForm = ({ + isOpen, + onToggle, + members = [], + projectSlug, + editValues +}: Props) => { + const { + control, + handleSubmit, + reset, + formState: { isSubmitting } + } = useForm({ + resolver: zodResolver(formSchema), + values: editValues ? { ...editValues, environment: editValues.environment.slug } : undefined + }); + const { currentWorkspace } = useWorkspace(); + + const environments = currentWorkspace?.environments || []; + useEffect(() => { + if (!isOpen) reset({}); + }, [isOpen]); + + const isEditMode = Boolean(editValues); + + const { mutateAsync: createAccessApprovalPolicy } = useCreateAccessApprovalPolicy(); + const { mutateAsync: updateAccessApprovalPolicy } = useUpdateAccessApprovalPolicy(); + + const handleCreatePolicy = async (data: TFormSchema) => { + try { + await createAccessApprovalPolicy({ + ...data, + projectSlug + }); + createNotification({ + type: "success", + text: "Successfully created policy" + }); + onToggle(false); + } catch (err) { + console.log(err); + createNotification({ + type: "error", + text: "Failed to create policy" + }); + } + }; + + const handleUpdatePolicy = async (data: TFormSchema) => { + if (!editValues?.id) return; + try { + await updateAccessApprovalPolicy({ + id: editValues?.id, + ...data, + projectSlug + }); + createNotification({ + type: "success", + text: "Successfully updated policy" + }); + onToggle(false); + } catch (err) { + console.log(err); + createNotification({ + type: "error", + text: "failed to update policy" + }); + } + }; + + const handleFormSubmit = async (data: TFormSchema) => { + if (isEditMode) { + await handleUpdatePolicy(data); + } else { + await handleCreatePolicy(data); + } + }; + + return ( + + +
+ ( + + + + )} + /> + ( + + + + )} + /> + + ( + + + + )} + /> + + ( + + + + + + + + Select members that are allowed to approve changes + + {members.map(({ id, user }) => { + const isChecked = value?.includes(id); + return ( + { + evt.preventDefault(); + onChange( + isChecked ? value?.filter((el) => el !== id) : [...(value || []), id] + ); + }} + key={`create-policy-members-${id}`} + iconPos="right" + icon={isChecked && } + > + {user.email} + + ); + })} + + + + )} + /> + ( + + field.onChange(parseInt(el.target.value, 10))} + /> + + )} + /> +
+ + +
+ +
+
+ ); +};