From 38adc83f2b6a4510188def4929df771504111294 Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Thu, 28 Mar 2024 20:54:25 -0700 Subject: [PATCH] Rename group fns and add upgrade plan modal to project level groups tab --- .../src/ee/services/group/group-service.ts | 18 +++++--------- backend/src/ee/services/group/group-types.ts | 6 ++--- frontend/src/hooks/api/groups/index.tsx | 6 ++--- frontend/src/hooks/api/groups/mutations.tsx | 4 ++-- frontend/src/hooks/api/groups/queries.tsx | 2 +- frontend/src/hooks/api/workspace/index.tsx | 2 +- frontend/src/hooks/api/workspace/queries.tsx | 2 +- .../OrgGroupsSection/OrgGroupMembersModal.tsx | 12 +++++----- .../components/GroupsSection/GroupModal.tsx | 24 ++++++++----------- .../GroupsSection/GroupsSection.tsx | 23 +++++++++++++++--- .../components/GroupsSection/GroupsTable.tsx | 4 ++-- 11 files changed, 55 insertions(+), 48 deletions(-) diff --git a/backend/src/ee/services/group/group-service.ts b/backend/src/ee/services/group/group-service.ts index 2b3e66e8d..55160e992 100644 --- a/backend/src/ee/services/group/group-service.ts +++ b/backend/src/ee/services/group/group-service.ts @@ -18,11 +18,11 @@ import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-p import { TPermissionServiceFactory } from "../permission/permission-service"; import { TGroupDALFactory } from "./group-dal"; import { + TAddUserToGroupDTO, TCreateGroupDTO, - TCreateGroupUserMembershipDTO, TDeleteGroupDTO, - TDeleteGroupUserMembershipDTO, - TGetGroupUserMembershipsDTO, + TListGroupUsersDTO, + TRemoveUserFromGroupDTO, TUpdateGroupDTO } from "./group-types"; import { TUserGroupMembershipDALFactory } from "./user-group-membership-dal"; @@ -185,13 +185,7 @@ export const groupServiceFactory = ({ return group; }; - const listGroupUsers = async ({ - groupSlug, - actor, - actorId, - actorAuthMethod, - actorOrgId - }: TGetGroupUserMembershipsDTO) => { + const listGroupUsers = async ({ groupSlug, actor, actorId, actorAuthMethod, actorOrgId }: TListGroupUsersDTO) => { if (!actorOrgId) throw new BadRequestError({ message: "Failed to create group without organization" }); const { permission } = await permissionService.getOrgPermission( @@ -224,7 +218,7 @@ export const groupServiceFactory = ({ actorId, actorAuthMethod, actorOrgId - }: TCreateGroupUserMembershipDTO) => { + }: TAddUserToGroupDTO) => { if (!actorOrgId) throw new BadRequestError({ message: "Failed to create group without organization" }); const { permission } = await permissionService.getOrgPermission( @@ -368,7 +362,7 @@ export const groupServiceFactory = ({ actorId, actorAuthMethod, actorOrgId - }: TDeleteGroupUserMembershipDTO) => { + }: TRemoveUserFromGroupDTO) => { if (!actorOrgId) throw new BadRequestError({ message: "Failed to create group without organization" }); const { permission } = await permissionService.getOrgPermission( diff --git a/backend/src/ee/services/group/group-types.ts b/backend/src/ee/services/group/group-types.ts index d465416f5..4e36e447e 100644 --- a/backend/src/ee/services/group/group-types.ts +++ b/backend/src/ee/services/group/group-types.ts @@ -19,16 +19,16 @@ export type TDeleteGroupDTO = { groupSlug: string; } & TGenericPermission; -export type TGetGroupUserMembershipsDTO = { +export type TListGroupUsersDTO = { groupSlug: string; } & TGenericPermission; -export type TCreateGroupUserMembershipDTO = { +export type TAddUserToGroupDTO = { groupSlug: string; username: string; } & TGenericPermission; -export type TDeleteGroupUserMembershipDTO = { +export type TRemoveUserFromGroupDTO = { groupSlug: string; username: string; } & TGenericPermission; diff --git a/frontend/src/hooks/api/groups/index.tsx b/frontend/src/hooks/api/groups/index.tsx index 4e2a0cb30..26b38d3a4 100644 --- a/frontend/src/hooks/api/groups/index.tsx +++ b/frontend/src/hooks/api/groups/index.tsx @@ -1,9 +1,9 @@ export { + useAddUserToGroup, useCreateGroup, - useCreateGroupUserMembership, useDeleteGroup, - useDeleteGroupUserMembership, + useRemoveUserFromGroup, useUpdateGroup} from "./mutations"; export { - useGetGroupUserMemberships + useListGroupUsers } from "./queries"; \ No newline at end of file diff --git a/frontend/src/hooks/api/groups/mutations.tsx b/frontend/src/hooks/api/groups/mutations.tsx index ccf167b51..e3d1ffedc 100644 --- a/frontend/src/hooks/api/groups/mutations.tsx +++ b/frontend/src/hooks/api/groups/mutations.tsx @@ -85,7 +85,7 @@ export const useDeleteGroup = () => { }); }; -export const useCreateGroupUserMembership = () => { +export const useAddUserToGroup = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: async ({ @@ -107,7 +107,7 @@ export const useCreateGroupUserMembership = () => { }); }; -export const useDeleteGroupUserMembership = () => { +export const useRemoveUserFromGroup = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: async ({ diff --git a/frontend/src/hooks/api/groups/queries.tsx b/frontend/src/hooks/api/groups/queries.tsx index aec0a92d9..910c8cdd3 100644 --- a/frontend/src/hooks/api/groups/queries.tsx +++ b/frontend/src/hooks/api/groups/queries.tsx @@ -15,7 +15,7 @@ type TUser = { isPartOfGroup: boolean; } -export const useGetGroupUserMemberships = (groupSlug: string) => { +export const useListGroupUsers = (groupSlug: string) => { return useQuery({ queryKey: groupKeys.getGroupUserMembership(groupSlug), queryFn: async () => { diff --git a/frontend/src/hooks/api/workspace/index.tsx b/frontend/src/hooks/api/workspace/index.tsx index 870c7a8f2..876a2db6c 100644 --- a/frontend/src/hooks/api/workspace/index.tsx +++ b/frontend/src/hooks/api/workspace/index.tsx @@ -13,12 +13,12 @@ export { useGetUserWorkspaces, useGetWorkspaceAuthorizations, useGetWorkspaceById, - useGetWorkspaceGroupMemberships, useGetWorkspaceIdentityMemberships, useGetWorkspaceIndexStatus, useGetWorkspaceIntegrations, useGetWorkspaceSecrets, useGetWorkspaceUsers, + useListWorkspaceGroups, useNameWorkspaceSecrets, useRenameWorkspace, useToggleAutoCapitalization, diff --git a/frontend/src/hooks/api/workspace/queries.tsx b/frontend/src/hooks/api/workspace/queries.tsx index 2122ab81a..d8600c4cf 100644 --- a/frontend/src/hooks/api/workspace/queries.tsx +++ b/frontend/src/hooks/api/workspace/queries.tsx @@ -526,7 +526,7 @@ export const useDeleteGroupFromWorkspace = () => { }); }; -export const useGetWorkspaceGroupMemberships = (projectSlug: string) => { +export const useListWorkspaceGroups = (projectSlug: string) => { return useQuery({ queryKey: workspaceKeys.getWorkspaceGroupMemberships(projectSlug), queryFn: async () => { diff --git a/frontend/src/views/Org/MembersPage/components/OrgGroupsTab/components/OrgGroupsSection/OrgGroupMembersModal.tsx b/frontend/src/views/Org/MembersPage/components/OrgGroupsTab/components/OrgGroupsSection/OrgGroupMembersModal.tsx index bd418d502..e45098ded 100644 --- a/frontend/src/views/Org/MembersPage/components/OrgGroupsTab/components/OrgGroupsSection/OrgGroupMembersModal.tsx +++ b/frontend/src/views/Org/MembersPage/components/OrgGroupsTab/components/OrgGroupsSection/OrgGroupMembersModal.tsx @@ -24,9 +24,9 @@ import { OrgPermissionSubjects } from "@app/context"; import { - useCreateGroupUserMembership, - useDeleteGroupUserMembership, - useGetGroupUserMemberships} from "@app/hooks/api"; + useAddUserToGroup, + useListGroupUsers, + useRemoveUserFromGroup} from "@app/hooks/api"; import { UsePopUpState } from "@app/hooks/usePopUp"; type Props = { @@ -44,9 +44,9 @@ export const OrgGroupMembersModal = ({ slug: string; }; - const { data: users, isLoading } = useGetGroupUserMemberships(popUpData?.slug ?? ""); - const { mutateAsync: assignMutateAsync } = useCreateGroupUserMembership(); - const { mutateAsync: unassignMutateAsync } = useDeleteGroupUserMembership(); + const { data: users, isLoading } = useListGroupUsers(popUpData?.slug ?? ""); + const { mutateAsync: assignMutateAsync } = useAddUserToGroup(); + const { mutateAsync: unassignMutateAsync } = useRemoveUserFromGroup(); const handleAssignment = async (username: string, assign: boolean) => { try { diff --git a/frontend/src/views/Project/MembersPage/components/GroupsTab/components/GroupsSection/GroupModal.tsx b/frontend/src/views/Project/MembersPage/components/GroupsTab/components/GroupsSection/GroupModal.tsx index 4e5b4d7ac..818c6eae0 100644 --- a/frontend/src/views/Project/MembersPage/components/GroupsTab/components/GroupsSection/GroupModal.tsx +++ b/frontend/src/views/Project/MembersPage/components/GroupsTab/components/GroupsSection/GroupModal.tsx @@ -1,8 +1,8 @@ import { useMemo } from "react"; import { Controller, useForm } from "react-hook-form"; import Link from "next/link"; -import { yupResolver } from "@hookform/resolvers/yup"; -import * as yup from "yup"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { z } from "zod"; import { createNotification } from "@app/components/notifications"; import { @@ -17,20 +17,16 @@ import { useAddGroupToWorkspace, useGetOrganizationGroups, useGetProjectRoles, - useGetWorkspaceGroupMemberships, + useListWorkspaceGroups, } from "@app/hooks/api"; import { UsePopUpState } from "@app/hooks/usePopUp"; -// TODO: change this to zod +const schema = z.object({ + slug: z.string(), + role: z.string() +}); -const schema = yup - .object({ - slug: yup.string().required("Group slug is required"), - role: yup.string() - }) - .required(); - -export type FormData = yup.InferType; +export type FormData = z.infer; type Props = { popUp: UsePopUpState<["group"]>; @@ -48,7 +44,7 @@ export const GroupModal = ({ const workspaceId = currentWorkspace?.id || ""; const { data: groups } = useGetOrganizationGroups(orgId); - const { data: groupMemberships } = useGetWorkspaceGroupMemberships(currentWorkspace?.slug || ""); + const { data: groupMemberships } = useListWorkspaceGroups(currentWorkspace?.slug || ""); const { data: roles } = useGetProjectRoles(workspaceId); @@ -70,7 +66,7 @@ export const GroupModal = ({ reset, formState: { isSubmitting } } = useForm({ - resolver: yupResolver(schema) + resolver: zodResolver(schema) }); const onFormSubmit = async ({ slug, role }: FormData) => { diff --git a/frontend/src/views/Project/MembersPage/components/GroupsTab/components/GroupsSection/GroupsSection.tsx b/frontend/src/views/Project/MembersPage/components/GroupsTab/components/GroupsSection/GroupsSection.tsx index 77c9bc4d2..b9638e644 100644 --- a/frontend/src/views/Project/MembersPage/components/GroupsTab/components/GroupsSection/GroupsSection.tsx +++ b/frontend/src/views/Project/MembersPage/components/GroupsTab/components/GroupsSection/GroupsSection.tsx @@ -5,9 +5,10 @@ import { createNotification } from "@app/components/notifications"; import { ProjectPermissionCan } from "@app/components/permissions"; import { Button, - DeleteActionModal + DeleteActionModal, + UpgradePlanModal } from "@app/components/v2"; -import { ProjectPermissionActions, ProjectPermissionSub,useWorkspace } from "@app/context"; +import { ProjectPermissionActions, ProjectPermissionSub, useSubscription,useWorkspace } from "@app/context"; import { usePopUp } from "@app/hooks"; import { useDeleteGroupFromWorkspace } from "@app/hooks/api"; @@ -15,6 +16,7 @@ import { GroupModal } from "./GroupModal"; import { GroupTable } from "./GroupsTable"; export const GroupsSection = () => { + const { subscription } = useSubscription(); const { currentWorkspace } = useWorkspace(); const { mutateAsync: deleteMutateAsync } = useDeleteGroupFromWorkspace(); @@ -25,6 +27,16 @@ export const GroupsSection = () => { "upgradePlan" ] as const); + const handleAddGroupModal = () => { + if (!subscription?.groups) { + handlePopUpOpen("upgradePlan", { + description: "You can manage users more efficiently with groups if you upgrade your Infisical plan." + }); + } else { + handlePopUpOpen("group"); + } + } + const onRemoveGroupSubmit = async (groupSlug: string) => { try { await deleteMutateAsync({ @@ -60,7 +72,7 @@ export const GroupsSection = () => { colorSchema="primary" type="submit" leftIcon={} - onClick={() => handlePopUpOpen("group")} + onClick={() => handleAddGroupModal()} isDisabled={!isAllowed} > Add Group @@ -83,6 +95,11 @@ export const GroupsSection = () => { ) } /> + handlePopUpToggle("upgradePlan", isOpen)} + text={(popUp.upgradePlan?.data as { description: string })?.description} + /> ); } \ No newline at end of file diff --git a/frontend/src/views/Project/MembersPage/components/GroupsTab/components/GroupsSection/GroupsTable.tsx b/frontend/src/views/Project/MembersPage/components/GroupsTab/components/GroupsSection/GroupsTable.tsx index 4958178d6..5906accab 100644 --- a/frontend/src/views/Project/MembersPage/components/GroupsTab/components/GroupsSection/GroupsTable.tsx +++ b/frontend/src/views/Project/MembersPage/components/GroupsTab/components/GroupsSection/GroupsTable.tsx @@ -16,7 +16,7 @@ import { Tr } from "@app/components/v2"; import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context"; -import { useGetWorkspaceGroupMemberships } from "@app/hooks/api"; +import { useListWorkspaceGroups } from "@app/hooks/api"; import { UsePopUpState } from "@app/hooks/usePopUp"; import { GroupRoles } from "./GroupRoles"; @@ -33,7 +33,7 @@ type Props = { export const GroupTable = ({ handlePopUpOpen }: Props) => { const { currentWorkspace } = useWorkspace(); - const { data, isLoading } = useGetWorkspaceGroupMemberships(currentWorkspace?.slug || ""); + const { data, isLoading } = useListWorkspaceGroups(currentWorkspace?.slug || ""); return (