From b64d4e57c49e2d3b63fb8196ceff6712711eae93 Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Mon, 22 Jul 2024 16:56:27 +0700 Subject: [PATCH] Clean org roles concept refactor --- frontend/src/hooks/api/roles/mutation.tsx | 44 ++- .../OrgRoleTabSection/OrgRoleTabSection.tsx | 4 +- .../OrgRoleTabSection/OrgRoleTable.tsx | 22 +- frontend/src/views/Org/RolePage/RolePage.tsx | 188 ++-------- .../components/RoleDetailsSection.tsx | 4 +- .../Org/RolePage/components/RoleModal.tsx | 35 +- .../components/RolePermissionModal.tsx | 350 ------------------ ...rmissionRow2.tsx => RolePermissionRow.tsx} | 19 +- .../RolePermissionsSection.tsx | 76 +--- .../RolePermissionsTable.tsx | 223 +++++------ .../RolePermissionsTable2.tsx | 103 ------ .../views/Org/RolePage/components/index.tsx | 1 - 12 files changed, 222 insertions(+), 847 deletions(-) delete mode 100644 frontend/src/views/Org/RolePage/components/RolePermissionModal.tsx rename frontend/src/views/Org/RolePage/components/RolePermissionsSection/{RolePermissionRow2.tsx => RolePermissionRow.tsx} (91%) delete mode 100644 frontend/src/views/Org/RolePage/components/RolePermissionsSection/RolePermissionsTable2.tsx diff --git a/frontend/src/hooks/api/roles/mutation.tsx b/frontend/src/hooks/api/roles/mutation.tsx index db94c7403..ef330e053 100644 --- a/frontend/src/hooks/api/roles/mutation.tsx +++ b/frontend/src/hooks/api/roles/mutation.tsx @@ -9,6 +9,7 @@ import { TCreateProjectRoleDTO, TDeleteOrgRoleDTO, TDeleteProjectRoleDTO, + TOrgRole, TUpdateOrgRoleDTO, TUpdateProjectRoleDTO } from "./types"; @@ -52,12 +53,17 @@ export const useDeleteProjectRole = () => { export const useCreateOrgRole = () => { const queryClient = useQueryClient(); - return useMutation({ - mutationFn: ({ orgId, permissions, ...dto }: TCreateOrgRoleDTO) => - apiRequest.post(`/api/v1/organization/${orgId}/roles`, { + return useMutation({ + mutationFn: async ({ orgId, permissions, ...dto }: TCreateOrgRoleDTO) => { + const { + data: { role } + } = await apiRequest.post(`/api/v1/organization/${orgId}/roles`, { ...dto, permissions: permissions.length ? packRules(permissions) : [] - }), + }); + + return role; + }, onSuccess: (_, { orgId }) => { queryClient.invalidateQueries(roleQueryKeys.getOrgRoles(orgId)); } @@ -67,19 +73,16 @@ export const useCreateOrgRole = () => { export const useUpdateOrgRole = () => { const queryClient = useQueryClient(); - return useMutation({ - mutationFn: ({ id, orgId, permissions, ...dto }: TUpdateOrgRoleDTO) => { - console.log("update args: ", { - id, - orgId, - permissions, - ...dto, - pack: permissions?.length ? packRules(permissions) : [] - }); - return apiRequest.patch(`/api/v1/organization/${orgId}/roles/${id}`, { + return useMutation({ + mutationFn: async ({ id, orgId, permissions, ...dto }: TUpdateOrgRoleDTO) => { + const { + data: { role } + } = await apiRequest.patch(`/api/v1/organization/${orgId}/roles/${id}`, { ...dto, permissions: permissions?.length ? packRules(permissions) : [] }); + + return role; }, onSuccess: (_, { id, orgId }) => { queryClient.invalidateQueries(roleQueryKeys.getOrgRoles(orgId)); @@ -91,11 +94,16 @@ export const useUpdateOrgRole = () => { export const useDeleteOrgRole = () => { const queryClient = useQueryClient(); - return useMutation({ - mutationFn: ({ orgId, id }: TDeleteOrgRoleDTO) => - apiRequest.delete(`/api/v1/organization/${orgId}/roles/${id}`, { + return useMutation({ + mutationFn: async ({ orgId, id }: TDeleteOrgRoleDTO) => { + const { + data: { role } + } = await apiRequest.delete(`/api/v1/organization/${orgId}/roles/${id}`, { data: { orgId } - }), + }); + + return role; + }, onSuccess: (_, { id, orgId }) => { queryClient.invalidateQueries(roleQueryKeys.getOrgRoles(orgId)); queryClient.invalidateQueries(roleQueryKeys.getOrgRole(orgId, id)); diff --git a/frontend/src/views/Org/MembersPage/components/OrgRoleTabSection/OrgRoleTabSection.tsx b/frontend/src/views/Org/MembersPage/components/OrgRoleTabSection/OrgRoleTabSection.tsx index 2d4dcd358..ef83bca9c 100644 --- a/frontend/src/views/Org/MembersPage/components/OrgRoleTabSection/OrgRoleTabSection.tsx +++ b/frontend/src/views/Org/MembersPage/components/OrgRoleTabSection/OrgRoleTabSection.tsx @@ -7,7 +7,7 @@ import { OrgRoleModifySection } from "./OrgRoleModifySection"; import { OrgRoleTable } from "./OrgRoleTable"; export const OrgRoleTabSection = () => { - const { popUp, handlePopUpOpen, handlePopUpClose } = usePopUp(["editRole"] as const); + const { popUp, handlePopUpClose } = usePopUp(["editRole"] as const); return popUp.editRole.isOpen ? ( { animate={{ opacity: 1, translateX: 0 }} exit={{ opacity: 0, translateX: -30 }} > - handlePopUpOpen("editRole", role)} /> + ); }; diff --git a/frontend/src/views/Org/MembersPage/components/OrgRoleTabSection/OrgRoleTable.tsx b/frontend/src/views/Org/MembersPage/components/OrgRoleTabSection/OrgRoleTable.tsx index dab3e8e74..904aec264 100644 --- a/frontend/src/views/Org/MembersPage/components/OrgRoleTabSection/OrgRoleTable.tsx +++ b/frontend/src/views/Org/MembersPage/components/OrgRoleTabSection/OrgRoleTable.tsx @@ -25,17 +25,15 @@ import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@a import { usePopUp } from "@app/hooks"; import { useDeleteOrgRole, useGetOrgRoles } from "@app/hooks/api"; import { TOrgRole } from "@app/hooks/api/roles/types"; +import { RoleModal } from "@app/views/Org/RolePage/components"; -type Props = { - onSelectRole: (role?: TOrgRole) => void; -}; - -export const OrgRoleTable = ({ onSelectRole }: Props) => { +export const OrgRoleTable = () => { const router = useRouter(); const { currentOrg } = useOrganization(); const orgId = currentOrg?.id || ""; const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ + "role", "deleteRole" ] as const); @@ -68,7 +66,10 @@ export const OrgRoleTable = ({ onSelectRole }: Props) => { colorSchema="primary" type="submit" leftIcon={} - onClick={() => onSelectRole()} + // onClick={() => onSelectRole()} + onClick={() => { + handlePopUpOpen("role"); + }} isDisabled={!isAllowed} > Add Role @@ -119,7 +120,8 @@ export const OrgRoleTable = ({ onSelectRole }: Props) => { )} onClick={(e) => { e.stopPropagation(); - onSelectRole(role); + router.push(`/org/${orgId}/roles/${id}`); + // onSelectRole(role); }} disabled={!isAllowed} > @@ -142,7 +144,10 @@ export const OrgRoleTable = ({ onSelectRole }: Props) => { ? "hover:!bg-red-500 hover:!text-white" : "pointer-events-none cursor-not-allowed opacity-50" )} - onClick={() => handlePopUpOpen("deleteRole", role)} + onClick={(e) => { + e.stopPropagation(); + handlePopUpOpen("deleteRole", role); + }} disabled={!isAllowed} > Delete Role @@ -158,6 +163,7 @@ export const OrgRoleTable = ({ onSelectRole }: Props) => { + { @@ -35,76 +29,40 @@ export const RolePage = withPermission( const { currentOrg } = useOrganization(); const orgId = currentOrg?.id || ""; const { data } = useGetOrgRole(orgId, roleId); - console.log("useGetOrgRole data: ", data); - - // const { data } = useGetIdentityById(identityId); // TODO: get role by id - // const { mutateAsync: deleteIdentity } = useDeleteIdentity(); - // const { mutateAsync: revokeToken } = useRevokeIdentityTokenAuthToken(); - // const { mutateAsync: revokeClientSecret } = useRevokeIdentityUniversalAuthClientSecret(); + const { mutateAsync: deleteOrgRole } = useDeleteOrgRole(); const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ "role", - "rolePermission" + "deleteOrgRole" ] as const); - // const onDeleteIdentitySubmit = async (id: string) => { - // try { - // await deleteIdentity({ - // identityId: id, - // organizationId: orgId - // }); + const onDeleteOrgRoleSubmit = async () => { + try { + if (!orgId || !roleId) return; - // createNotification({ - // text: "Successfully deleted identity", - // type: "success" - // }); + await deleteOrgRole({ + orgId, + id: roleId + }); - // handlePopUpClose("deleteIdentity"); - // router.push(`/org/${orgId}/members`); - // } catch (err) { - // console.error(err); - // const error = err as any; - // const text = error?.response?.data?.message ?? "Failed to delete identity"; + createNotification({ + text: "Successfully deleted organization role", + type: "success" + }); - // createNotification({ - // text, - // type: "error" - // }); - // } - // }; + handlePopUpClose("deleteOrgRole"); + router.push(`/org/${orgId}/members`); + } catch (err) { + console.error(err); + const error = err as any; + const text = error?.response?.data?.message ?? "Failed to delete organization role"; - // const onRevokeTokenSubmit = async ({ - // identityId: parentIdentityId, - // tokenId, - // name - // }: { - // identityId: string; - // tokenId: string; - // name: string; - // }) => { - // try { - // await revokeToken({ - // identityId: parentIdentityId, - // tokenId - // }); - - // handlePopUpClose("revokeToken"); - - // createNotification({ - // text: `Successfully revoked token ${name ?? ""}`, - // type: "success" - // }); - // } catch (err) { - // console.error(err); - // const error = err as any; - // const text = error?.response?.data?.message ?? "Failed to delete identity"; - - // createNotification({ - // text, - // type: "error" - // }); - // } - // }; + createNotification({ + text, + type: "error" + }); + } + }; return (
@@ -139,12 +97,9 @@ export const RolePage = withPermission( !isAllowed && "pointer-events-none cursor-not-allowed opacity-50" )} onClick={async () => { - // handlePopUpOpen("identity", { - // identityId, - // name: data.identity.name, - // role: data.role, - // customRole: data.customRole - // }); + handlePopUpOpen("role", { + roleId + }); }} disabled={!isAllowed} > @@ -161,10 +116,7 @@ export const RolePage = withPermission( : "pointer-events-none cursor-not-allowed opacity-50" )} onClick={async () => { - // handlePopUpOpen("deleteIdentity", { - // identityId, - // name: data.identity.name - // }); + handlePopUpOpen("deleteOrgRole"); }} disabled={!isAllowed} > @@ -179,84 +131,18 @@ export const RolePage = withPermission(
- +
)} - - {/* - - - - - - handlePopUpToggle("upgradePlan", isOpen)} - text={(popUp.upgradePlan?.data as { description: string })?.description} - /> handlePopUpToggle("deleteIdentity", isOpen)} + isOpen={popUp.deleteOrgRole.isOpen} + title={`Are you sure want to delete the organization role ${data?.name ?? ""}?`} + onChange={(isOpen) => handlePopUpToggle("deleteOrgRole", isOpen)} deleteKey="confirm" - onDeleteApproved={() => - onDeleteIdentitySubmit( - (popUp?.deleteIdentity?.data as { identityId: string })?.identityId - ) - } + onDeleteApproved={() => onDeleteOrgRoleSubmit()} /> - handlePopUpToggle("revokeToken", isOpen)} - deleteKey="confirm" - onDeleteApproved={() => { - const revokeTokenData = popUp?.revokeToken?.data as { - identityId: string; - tokenId: string; - name: string; - }; - - return onRevokeTokenSubmit(revokeTokenData); - }} - /> - handlePopUpToggle("revokeClientSecret", isOpen)} - deleteKey="confirm" - onDeleteApproved={() => { - const deleteClientSecretData = popUp?.revokeClientSecret?.data as { - clientSecretId: string; - clientSecretPrefix: string; - }; - - return onDeleteClientSecretSubmit({ - clientSecretId: deleteClientSecretData.clientSecretId - }); - }} - /> */} ); }, diff --git a/frontend/src/views/Org/RolePage/components/RoleDetailsSection.tsx b/frontend/src/views/Org/RolePage/components/RoleDetailsSection.tsx index cad447b8e..4142569fd 100644 --- a/frontend/src/views/Org/RolePage/components/RoleDetailsSection.tsx +++ b/frontend/src/views/Org/RolePage/components/RoleDetailsSection.tsx @@ -80,7 +80,9 @@ export const RoleDetailsSection = ({ roleId, handlePopUpOpen }: Props) => {

Description

-

{data.description}

+

+ {data.description?.length ? data.description : "-"} +

diff --git a/frontend/src/views/Org/RolePage/components/RoleModal.tsx b/frontend/src/views/Org/RolePage/components/RoleModal.tsx index 28816b6fa..ab909d931 100644 --- a/frontend/src/views/Org/RolePage/components/RoleModal.tsx +++ b/frontend/src/views/Org/RolePage/components/RoleModal.tsx @@ -1,13 +1,13 @@ import { useEffect } from "react"; import { Controller, useForm } from "react-hook-form"; +import { useRouter } from "next/router"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; -// import { useRouter } from "next/router"; import { createNotification } from "@app/components/notifications"; import { Button, FormControl, Input, Modal, ModalContent } from "@app/components/v2"; import { useOrganization } from "@app/context"; -import { useGetOrgRole, useUpdateOrgRole } from "@app/hooks/api"; +import { useCreateOrgRole, useGetOrgRole, useUpdateOrgRole } from "@app/hooks/api"; import { UsePopUpState } from "@app/hooks/usePopUp"; const schema = z @@ -22,19 +22,11 @@ export type FormData = z.infer; type Props = { popUp: UsePopUpState<["role"]>; - // handlePopUpOpen: ( - // popUpName: keyof UsePopUpState<["identityAuthMethod"]>, - // data: { - // identityId: string; - // name: string; - // authMethod?: IdentityAuthMethod; - // } - // ) => void; handlePopUpToggle: (popUpName: keyof UsePopUpState<["role"]>, state?: boolean) => void; }; export const RoleModal = ({ popUp, handlePopUpToggle }: Props) => { - // const router = useRouter(); + const router = useRouter(); const { currentOrg } = useOrganization(); const orgId = currentOrg?.id || ""; @@ -44,10 +36,7 @@ export const RoleModal = ({ popUp, handlePopUpToggle }: Props) => { const { data: role } = useGetOrgRole(orgId, popupData?.roleId ?? ""); - // const { mutateAsync: createMutateAsync } = useCreateIdentity(); - // const { mutateAsync: updateMutateAsync } = useUpdateIdentity(); - // const { mutateAsync: addMutateAsync } = useAddIdentityUniversalAuth(); - + const { mutateAsync: createOrgRole } = useCreateOrgRole(); const { mutateAsync: updateOrgRole } = useUpdateOrgRole(); const { @@ -102,16 +91,18 @@ export const RoleModal = ({ popUp, handlePopUpToggle }: Props) => { handlePopUpToggle("role", false); } else { - // TODO: create + // create - // const { id: createdId } = await createMutateAsync({ - // name, - // role: role || undefined, - // organizationId: orgId - // }); + const newRole = await createOrgRole({ + orgId, + name, + description, + slug, + permissions: [] + }); handlePopUpToggle("role", false); - // router.push(`/org/${orgId}/identities/${createdId}`); + router.push(`/org/${orgId}/roles/${newRole.id}`); } createNotification({ diff --git a/frontend/src/views/Org/RolePage/components/RolePermissionModal.tsx b/frontend/src/views/Org/RolePage/components/RolePermissionModal.tsx deleted file mode 100644 index 18347bd57..000000000 --- a/frontend/src/views/Org/RolePage/components/RolePermissionModal.tsx +++ /dev/null @@ -1,350 +0,0 @@ -import { useEffect } from "react"; -import { Controller, useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { z } from "zod"; - -import { createNotification } from "@app/components/notifications"; -import { - Button, - Checkbox, - FormControl, - Modal, - ModalContent, - Select, - SelectItem -} from "@app/components/v2"; -import { useOrganization } from "@app/context"; -import { useGetOrgRole } from "@app/hooks/api"; -import { UsePopUpState } from "@app/hooks/usePopUp"; - -enum Permission { - NoAccess = "no-access", - ReadOnly = "read-only", - FullAccess = "full-acess", - Custom = "custom" -} - -const generalPermissionSchema = z - .object({ - read: z.boolean().optional(), - edit: z.boolean().optional(), - delete: z.boolean().optional(), - create: z.boolean().optional() - }) - .optional(); - -const specificPermissionSchemas = { - workspace: z - .object({ - read: z.boolean().optional(), - create: z.boolean().optional() - }) - .optional(), - member: generalPermissionSchema, - groups: generalPermissionSchema, - role: generalPermissionSchema, - settings: generalPermissionSchema, - "service-account": generalPermissionSchema, - "incident-contact": generalPermissionSchema, - "secret-scanning": generalPermissionSchema, - sso: generalPermissionSchema, - scim: generalPermissionSchema, - ldap: generalPermissionSchema, - billing: generalPermissionSchema, - identity: generalPermissionSchema -}; - -// Create a union of all possible keys -const permissionsUnion = z.union([ - z.object({ workspace: specificPermissionSchemas.workspace }), - z.object({ member: specificPermissionSchemas.member }), - z.object({ groups: specificPermissionSchemas.groups }), - z.object({ role: specificPermissionSchemas.role }), - z.object({ settings: specificPermissionSchemas.settings }), - z.object({ "service-account": specificPermissionSchemas["service-account"] }), - z.object({ "incident-contact": specificPermissionSchemas["incident-contact"] }), - z.object({ "secret-scanning": specificPermissionSchemas["secret-scanning"] }), - z.object({ sso: specificPermissionSchemas.sso }), - z.object({ scim: specificPermissionSchemas.scim }), - z.object({ ldap: specificPermissionSchemas.ldap }), - z.object({ billing: specificPermissionSchemas.billing }), - z.object({ identity: specificPermissionSchemas.identity }) -]); - -const schema = z.object({ - resource: z.string(), // this is formName - action: z.nativeEnum(Permission), - permissions: z.record(z.string(), permissionsUnion).optional() -}); - -type FormData = z.infer; - -type Props = { - roleId: string; - popUp: UsePopUpState<["rolePermission"]>; - handlePopUpToggle: (popUpName: keyof UsePopUpState<["rolePermission"]>, state?: boolean) => void; -}; - -const SIMPLE_PERMISSION_OPTIONS = [ - { - title: "User management", - formName: "member" - }, - { - title: "Group management", - formName: "groups" - }, - { - title: "Machine identity management", - formName: "identity" - }, - { - title: "Billing & usage", - formName: "billing" - }, - { - title: "Role management", - formName: "role" - }, - { - title: "Incident Contacts", - formName: "incident-contact" - }, - { - title: "Organization profile", - formName: "settings" - }, - { - title: "Secret Scanning", - formName: "secret-scanning" - }, - { - title: "SSO", - formName: "sso" - }, - { - title: "LDAP", - formName: "ldap" - }, - { - title: "SCIM", - formName: "scim" - } -] as const; - -const PERMISSIONS = [ - { action: "read", label: "View" }, - { action: "create", label: "Create" }, - { action: "edit", label: "Modify" }, - { action: "delete", label: "Remove" } -] as const; - -const SECRET_SCANNING_PERMISSIONS = [ - { action: "read", label: "View risks" }, - { action: "create", label: "Add integrations" }, - { action: "edit", label: "Edit risk status" }, - { action: "delete", label: "Remove integrations" } -] as const; - -const INCIDENT_CONTACTS_PERMISSIONS = [ - { action: "read", label: "View contacts" }, - { action: "create", label: "Add new contacts" }, - { action: "edit", label: "Edit contacts" }, - { action: "delete", label: "Remove contacts" } -] as const; - -const MEMBERS_PERMISSIONS = [ - { action: "read", label: "View all members" }, - { action: "create", label: "Invite members" }, - { action: "edit", label: "Edit members" }, - { action: "delete", label: "Remove members" } -] as const; - -const BILLING_PERMISSIONS = [ - { action: "read", label: "View bills" }, - { action: "create", label: "Add payment methods" }, - { action: "edit", label: "Edit payments" }, - { action: "delete", label: "Remove payments" } -] as const; - -const getPermissionList = (option: string) => { - switch (option) { - case "secret-scanning": - return SECRET_SCANNING_PERMISSIONS; - case "billing": - return BILLING_PERMISSIONS; - case "incident-contact": - return INCIDENT_CONTACTS_PERMISSIONS; - case "member": - return MEMBERS_PERMISSIONS; - default: - return PERMISSIONS; - } -}; - -export const RolePermissionModal = ({ roleId, popUp, handlePopUpToggle }: Props) => { - const { currentOrg } = useOrganization(); - const orgId = currentOrg?.id || ""; - const { data: role } = useGetOrgRole(orgId, roleId); - - const { - control, - handleSubmit, - reset, - formState: { isSubmitting }, - watch - } = useForm({ - resolver: zodResolver(schema) - }); - - const resource = watch("resource"); - const action = watch("action"); - - useEffect(() => { - reset({ - resource: SIMPLE_PERMISSION_OPTIONS[0].formName, - action: Permission.NoAccess - }); - - // TODO: update for existing permission - - // if (role) { - // console.log("existing role found: ", role); - // reset({ - // resource: SIMPLE_PERMISSION_OPTIONS[0].formName - // }); - // } else { - // console.log("no role found"); - // reset({ - // resource: SIMPLE_PERMISSION_OPTIONS[0].formName - // }); - // } - }, [role]); - - const onFormSubmit = async () => { - try { - // TODO: map action to permission array? - - // TODO: add permission to role - - // await addIdentityToWorkspace({ - // workspaceId, - // identityId, - // role: role || undefined - // }); - - createNotification({ - text: "Successfully added permission to role", - type: "success" - }); - - // reset(); - // handlePopUpToggle("rolePermission", false); - } catch (err) { - console.error(err); - const error = err as any; - const text = error?.response?.data?.message ?? "Failed to add identity to project"; - - createNotification({ - text, - type: "error" - }); - } - }; - - return ( - { - handlePopUpToggle("rolePermission", isOpen); - reset(); - }} - > - -
- ( - - - - )} - /> - ( - - - - )} - /> - {action === Permission.Custom && ( -
- {getPermissionList(watch("resource")).map((p) => { - return ( - ( - - {p.label} - - )} - /> - ); - })} -
- )} -
- - -
- -
-
- ); -}; diff --git a/frontend/src/views/Org/RolePage/components/RolePermissionsSection/RolePermissionRow2.tsx b/frontend/src/views/Org/RolePage/components/RolePermissionsSection/RolePermissionRow.tsx similarity index 91% rename from frontend/src/views/Org/RolePage/components/RolePermissionsSection/RolePermissionRow2.tsx rename to frontend/src/views/Org/RolePage/components/RolePermissionsSection/RolePermissionRow.tsx index 2d56f1e47..60c181d17 100644 --- a/frontend/src/views/Org/RolePage/components/RolePermissionsSection/RolePermissionRow2.tsx +++ b/frontend/src/views/Org/RolePage/components/RolePermissionsSection/RolePermissionRow.tsx @@ -3,8 +3,7 @@ import { Control, Controller, UseFormSetValue, useWatch } from "react-hook-form" import { faChevronDown, faChevronRight } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { createNotification } from "@app/components/notifications"; -import { Checkbox, IconButton, Select, SelectItem,Td, Tr } from "@app/components/v2"; +import { Checkbox, IconButton, Select, SelectItem, Td, Tr } from "@app/components/v2"; import { useToggle } from "@app/hooks"; import { TFormSchema } from "@app/views/Org/MembersPage/components/OrgRoleTabSection/OrgRoleModifySection/OrgRoleModifySection.utils"; @@ -63,6 +62,7 @@ type Props = { formName: keyof Omit, "workspace">; setValue: UseFormSetValue; control: Control; + handleSubmit: () => void; }; // permission categories @@ -76,7 +76,7 @@ enum Permission { // TODO: support for default roles -export const RolePermissionRow2 = ({ title, formName, control, setValue }: Props) => { +export const RolePermissionRow = ({ title, formName, handleSubmit, control, setValue }: Props) => { const [isRowExpanded, setIsRowExpanded] = useToggle(); const [isCustom, setIsCustom] = useToggle(); @@ -111,7 +111,6 @@ export const RolePermissionRow2 = ({ title, formName, control, setValue }: Props }, []); const handlePermissionChange = (val: Permission) => { - // TODO: trigger update if (val === Permission.Custom) { setIsRowExpanded.on(); setIsCustom.on(); @@ -150,12 +149,15 @@ export const RolePermissionRow2 = ({ title, formName, control, setValue }: Props break; } - createNotification({ type: "success", text: "Updated permission on role." }); + handleSubmit(); }; return ( <> - + setIsRowExpanded.toggle()} + > ( { + field.onChange(e); + handleSubmit(); + }} id={`permissions.${formName}.${action}`} > {label} diff --git a/frontend/src/views/Org/RolePage/components/RolePermissionsSection/RolePermissionsSection.tsx b/frontend/src/views/Org/RolePage/components/RolePermissionsSection/RolePermissionsSection.tsx index 6f5220242..2b7d43a7d 100644 --- a/frontend/src/views/Org/RolePage/components/RolePermissionsSection/RolePermissionsSection.tsx +++ b/frontend/src/views/Org/RolePage/components/RolePermissionsSection/RolePermissionsSection.tsx @@ -1,88 +1,18 @@ -// import { UsePopUpState } from "@app/hooks/usePopUp"; -import { RolePermissionsTable2 } from "./RolePermissionsTable2"; +import { RolePermissionsTable } from "./RolePermissionsTable"; type Props = { roleId: string; - // handlePopUpOpen: (popUpName: keyof UsePopUpState<["rolePermission"]>, data?: {}) => void; }; -export const RolePermissionsSection = ({ - roleId -}: // handlePopUpOpen -Props) => { - // const { mutateAsync: deleteMutateAsync } = useDeleteIdentityFromWorkspace(); - - // const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ - // "addIdentityToProject", - // "removeIdentityFromProject" - // ] as const); - - // const onRemoveIdentitySubmit = async (id: string, projectId: string) => { - // try { - // await deleteMutateAsync({ - // identityId: id, - // workspaceId: projectId - // }); - - // createNotification({ - // text: "Successfully removed identity from project", - // type: "success" - // }); - - // handlePopUpClose("removeIdentityFromProject"); - // } catch (err) { - // console.error(err); - // const error = err as any; - // const text = error?.response?.data?.message ?? "Failed to remove identity from project"; - - // createNotification({ - // text, - // type: "error" - // }); - // } - // }; - +export const RolePermissionsSection = ({ roleId }: Props) => { return (

Permissions

- {/* handlePopUpOpen("rolePermission")} - > - - */}
- {/* */} - - {/* */} +
- {/* handlePopUpToggle("removeIdentityFromProject", isOpen)} - deleteKey="confirm" - onDeleteApproved={() => { - const popupData = popUp?.removeIdentityFromProject?.data as { - identityId: string; - projectId: string; - }; - - return onRemoveIdentitySubmit(popupData.identityId, popupData.projectId); - }} - /> */} - {/* */}
); }; diff --git a/frontend/src/views/Org/RolePage/components/RolePermissionsSection/RolePermissionsTable.tsx b/frontend/src/views/Org/RolePage/components/RolePermissionsSection/RolePermissionsTable.tsx index a4c7c9509..ac485b47b 100644 --- a/frontend/src/views/Org/RolePage/components/RolePermissionsSection/RolePermissionsTable.tsx +++ b/frontend/src/views/Org/RolePage/components/RolePermissionsSection/RolePermissionsTable.tsx @@ -1,124 +1,125 @@ -import { faEllipsis } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { twMerge } from "tailwind-merge"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; -import { OrgPermissionCan } from "@app/components/permissions"; +import { createNotification } from "@app/components/notifications"; +import { Table, TableContainer, TBody, Th, THead, Tr } from "@app/components/v2"; +import { useOrganization } from "@app/context"; +import { useGetOrgRole, useUpdateOrgRole } from "@app/hooks/api"; import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, - // Tooltip, - // IconButton, - // EmptyState, - Table, - TableContainer, - // TableSkeleton, - TBody, - Td, - Th, - THead, - Tr} from "@app/components/v2"; -import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context"; -// import { UsePopUpState } from "@app/hooks/usePopUp"; + formRolePermission2API, + formSchema, + rolePermission2Form, + TFormSchema +} from "@app/views/Org/MembersPage/components/OrgRoleTabSection/OrgRoleModifySection/OrgRoleModifySection.utils"; -// import { IdentityProjectRow } from "./IdentityProjectRow"; +import { RolePermissionRow } from "./RolePermissionRow"; -// type Props = { -// identityId: string; -// handlePopUpOpen: ( -// popUpName: keyof UsePopUpState<["removeIdentityFromProject"]>, -// data?: {} -// ) => void; -// }; +const SIMPLE_PERMISSION_OPTIONS = [ + { + title: "User management", + formName: "member" + }, + { + title: "Group management", + formName: "groups" + }, + { + title: "Machine identity management", + formName: "identity" + }, + { + title: "Billing & usage", + formName: "billing" + }, + { + title: "Role management", + formName: "role" + }, + { + title: "Incident Contacts", + formName: "incident-contact" + }, + { + title: "Organization profile", + formName: "settings" + }, + { + title: "Secret Scanning", + formName: "secret-scanning" + }, + { + title: "SSO", + formName: "sso" + }, + { + title: "LDAP", + formName: "ldap" + }, + { + title: "SCIM", + formName: "scim" + } +] as const; + +type Props = { + roleId: string; +}; + +export const RolePermissionsTable = ({ roleId }: Props) => { + const { currentOrg } = useOrganization(); + const orgId = currentOrg?.id || ""; + + const { data: role } = useGetOrgRole(orgId, roleId); + + const { setValue, control, handleSubmit } = useForm({ + defaultValues: role ? { ...role, permissions: rolePermission2Form(role.permissions) } : {}, + resolver: zodResolver(formSchema) + }); + + const { mutateAsync: updateRole } = useUpdateOrgRole(); + + const onSubmit = async (el: TFormSchema) => { + try { + await updateRole({ + orgId, + id: roleId, + ...el, + permissions: formRolePermission2API(el.permissions) + }); + createNotification({ type: "success", text: "Successfully updated role" }); + } catch (err) { + console.log(err); + createNotification({ type: "error", text: "Failed to update role" }); + } + }; -export const RolePermissionsTable = () => { - // const { data: projectMemberships, isLoading } = useGetIdentityProjectMemberships(identityId); return ( - - - - - - - - - - - - - - {/* */} - {/* {isLoading && } */} - {/* {!isLoading && - projectMemberships?.map((membership) => { +
+
ResourceAllowed Actions -
IdentityCreate/Read - - -
- -
-
- - - {(isAllowed) => ( - { - e.stopPropagation(); - // TODO - }} - disabled={!isAllowed} - > - Edit Permission - - )} - - - {(isAllowed) => ( - { - e.stopPropagation(); - // handlePopUpOpen("deleteIdentity", { - // identityId: id, - // name - // }); - }} - disabled={!isAllowed} - > - Delete Permission - - )} - - -
-
+ + + + + + + + {SIMPLE_PERMISSION_OPTIONS.map((permission) => { return ( -
Row
- ); - })} */} - -
+ ResourcePermission
- {/* */} - {/* {!isLoading && !projectMemberships?.length && ( - - )} */} + })} + + +
); }; diff --git a/frontend/src/views/Org/RolePage/components/RolePermissionsSection/RolePermissionsTable2.tsx b/frontend/src/views/Org/RolePage/components/RolePermissionsSection/RolePermissionsTable2.tsx deleted file mode 100644 index aa55247e8..000000000 --- a/frontend/src/views/Org/RolePage/components/RolePermissionsSection/RolePermissionsTable2.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import { useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; - -import { Table, TableContainer, TBody, Th, THead, Tr } from "@app/components/v2"; -import { useOrganization } from "@app/context"; -import { useGetOrgRole } from "@app/hooks/api"; -// TODO: consider moving this out -import { - formSchema, - rolePermission2Form, - TFormSchema} from "@app/views/Org/MembersPage/components/OrgRoleTabSection/OrgRoleModifySection/OrgRoleModifySection.utils"; - -import { RolePermissionRow2 } from "./RolePermissionRow2"; - -const SIMPLE_PERMISSION_OPTIONS = [ - { - title: "User management", - formName: "member" - }, - { - title: "Group management", - formName: "groups" - }, - { - title: "Machine identity management", - formName: "identity" - }, - { - title: "Billing & usage", - formName: "billing" - }, - { - title: "Role management", - formName: "role" - }, - { - title: "Incident Contacts", - formName: "incident-contact" - }, - { - title: "Organization profile", - formName: "settings" - }, - { - title: "Secret Scanning", - formName: "secret-scanning" - }, - { - title: "SSO", - formName: "sso" - }, - { - title: "LDAP", - formName: "ldap" - }, - { - title: "SCIM", - formName: "scim" - } -] as const; - -type Props = { - roleId: string; -}; - -export const RolePermissionsTable2 = ({ roleId }: Props) => { - const { currentOrg } = useOrganization(); - const orgId = currentOrg?.id || ""; - - const { data: role } = useGetOrgRole(orgId, roleId); - - const { setValue, control } = useForm({ - defaultValues: role ? { ...role, permissions: rolePermission2Form(role.permissions) } : {}, - resolver: zodResolver(formSchema) - }); - - return ( - - - - - - - - - - {SIMPLE_PERMISSION_OPTIONS.map((permission) => { - return ( - - ); - })} - -
- ResourcePermission
-
- ); -}; diff --git a/frontend/src/views/Org/RolePage/components/index.tsx b/frontend/src/views/Org/RolePage/components/index.tsx index dd44ac103..3f57cd670 100644 --- a/frontend/src/views/Org/RolePage/components/index.tsx +++ b/frontend/src/views/Org/RolePage/components/index.tsx @@ -1,4 +1,3 @@ export { RoleDetailsSection } from "./RoleDetailsSection"; export { RoleModal } from "./RoleModal"; -export { RolePermissionModal } from "./RolePermissionModal"; export { RolePermissionsSection } from "./RolePermissionsSection";