diff --git a/backend/src/ee/services/group/group-dal.ts b/backend/src/ee/services/group/group-dal.ts index e9fae3982..0f18f932f 100644 --- a/backend/src/ee/services/group/group-dal.ts +++ b/backend/src/ee/services/group/group-dal.ts @@ -1,11 +1,46 @@ +import { Knex } from "knex"; + import { TDbClient } from "@app/db"; import { TableName } from "@app/db/schemas"; -import { ormify } from "@app/lib/knex"; +import { DatabaseError } from "@app/lib/errors"; +import { ormify, selectAllTableCols } from "@app/lib/knex"; export type TGroupDALFactory = ReturnType; export const groupDALFactory = (db: TDbClient) => { const groupOrm = ormify(db, TableName.Groups); - return { ...groupOrm }; + const findByOrgId = async (orgId: string, tx?: Knex) => { + try { + const docs = await (tx || db)(TableName.Groups) + .where(`${TableName.Groups}.orgId`, orgId) + .leftJoin(TableName.OrgRoles, `${TableName.Groups}.roleId`, `${TableName.OrgRoles}.id`) + .select(selectAllTableCols(TableName.Groups)) + // cr stands for custom role + .select(db.ref("id").as("crId").withSchema(TableName.OrgRoles)) + .select(db.ref("name").as("crName").withSchema(TableName.OrgRoles)) + .select(db.ref("slug").as("crSlug").withSchema(TableName.OrgRoles)) + .select(db.ref("description").as("crDescription").withSchema(TableName.OrgRoles)) + .select(db.ref("permissions").as("crPermission").withSchema(TableName.OrgRoles)); + return docs.map(({ crId, crDescription, crSlug, crPermission, crName, ...el }) => ({ + ...el, + customRole: el.roleId + ? { + id: crId, + name: crName, + slug: crSlug, + permissions: crPermission, + description: crDescription + } + : undefined + })); + } catch (error) { + throw new DatabaseError({ error, name: "FindByOrgId" }); + } + }; + + return { + findByOrgId, + ...groupOrm + }; }; diff --git a/backend/src/ee/services/group/group-service.ts b/backend/src/ee/services/group/group-service.ts index f0c5514bc..725e478b2 100644 --- a/backend/src/ee/services/group/group-service.ts +++ b/backend/src/ee/services/group/group-service.ts @@ -105,7 +105,7 @@ export const groupServiceFactory = ({ groupDAL, permissionService, licenseServic ...(role ? { role: customRole ? OrgMembershipRole.Custom : role, - roleId: customRole?.id + roleId: customRole?.id ?? null } : {}) } diff --git a/backend/src/server/routes/v1/organization-router.ts b/backend/src/server/routes/v1/organization-router.ts index 4a008caa1..c015fd899 100644 --- a/backend/src/server/routes/v1/organization-router.ts +++ b/backend/src/server/routes/v1/organization-router.ts @@ -5,6 +5,7 @@ import { IncidentContactsSchema, OrganizationsSchema, OrgMembershipsSchema, + OrgRolesSchema, UsersSchema } from "@app/db/schemas"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; @@ -212,7 +213,17 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => { }), response: { 200: z.object({ - groups: GroupsSchema.array() + groups: GroupsSchema.merge( + z.object({ + customRole: OrgRolesSchema.pick({ + id: true, + name: true, + slug: true, + permissions: true, + description: true + }).optional() + }) + ).array() }) } }, diff --git a/backend/src/server/routes/v2/identity-org-router.ts b/backend/src/server/routes/v2/identity-org-router.ts index 440130e13..16a0ea8c4 100644 --- a/backend/src/server/routes/v2/identity-org-router.ts +++ b/backend/src/server/routes/v2/identity-org-router.ts @@ -46,6 +46,7 @@ export const registerIdentityOrgRouter = async (server: FastifyZodProvider) => { actorOrgId: req.permission.orgId, orgId: req.params.orgId }); + return { identityMemberships }; } }); diff --git a/backend/src/services/org/org-service.ts b/backend/src/services/org/org-service.ts index da8d4e561..fab94edf6 100644 --- a/backend/src/services/org/org-service.ts +++ b/backend/src/services/org/org-service.ts @@ -119,7 +119,7 @@ export const orgServiceFactory = ({ const getOrgGroups = async ({ actor, actorId, orgId, actorAuthMethod, actorOrgId }: TGetOrgGroupsDTO) => { await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId); - const groups = await groupDAL.find({ orgId }); + const groups = await groupDAL.findByOrgId(orgId); return groups; }; diff --git a/frontend/src/hooks/api/groups/types.ts b/frontend/src/hooks/api/groups/types.ts index 6fb88c4b8..fb495665c 100644 --- a/frontend/src/hooks/api/groups/types.ts +++ b/frontend/src/hooks/api/groups/types.ts @@ -1,3 +1,9 @@ +import { TOrgRole } from "../roles/types"; + +export type TGroupOrgMembership = TGroup & { + customRole?: TOrgRole; +} + export type TGroup = { id: string; name: string; diff --git a/frontend/src/hooks/api/organization/queries.tsx b/frontend/src/hooks/api/organization/queries.tsx index 0140388e2..aee5b1ebf 100644 --- a/frontend/src/hooks/api/organization/queries.tsx +++ b/frontend/src/hooks/api/organization/queries.tsx @@ -2,7 +2,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { apiRequest } from "@app/config/request"; -import { TGroup } from "../groups/types"; +import { TGroupOrgMembership } from "../groups/types"; import { IdentityMembershipOrg } from "../identities/types"; import { BillingDetails, @@ -412,7 +412,7 @@ export const useGetOrganizationGroups = (organizationId: string) => { queryFn: async () => { const { data: { groups } - } = await apiRequest.get<{ groups: TGroup[] }>(`/api/v1/organization/${organizationId}/groups`); + } = await apiRequest.get<{ groups: TGroupOrgMembership[] }>(`/api/v1/organization/${organizationId}/groups`); return groups; } diff --git a/frontend/src/views/Org/MembersPage/components/OrgGroupsTab/components/OrgGroupsSection/OrgGroupsTable.tsx b/frontend/src/views/Org/MembersPage/components/OrgGroupsTab/components/OrgGroupsSection/OrgGroupsTable.tsx index 3cc217526..2fea1105f 100644 --- a/frontend/src/views/Org/MembersPage/components/OrgGroupsTab/components/OrgGroupsSection/OrgGroupsTable.tsx +++ b/frontend/src/views/Org/MembersPage/components/OrgGroupsTab/components/OrgGroupsSection/OrgGroupsTable.tsx @@ -1,16 +1,15 @@ import { useState } from "react"; -// import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider"; import { faMagnifyingGlass, faPencil, faUsers, faXmark } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider"; import { OrgPermissionCan } from "@app/components/permissions"; import { -// Button, EmptyState, IconButton, Input, -// Select, -// SelectItem, + Select, + SelectItem, Table, TableContainer, TableSkeleton, @@ -19,14 +18,16 @@ import { Th, THead, Tooltip, - Tr} from "@app/components/v2"; + Tr +} from "@app/components/v2"; import { OrgPermissionActions, OrgPermissionSubjects, useOrganization} from "@app/context"; import { useGetOrganizationGroups, - // useGetOrgRoles + useGetOrgRoles, + useUpdateGroup } from "@app/hooks/api"; import { UsePopUpState } from "@app/hooks/usePopUp"; @@ -39,6 +40,11 @@ type Props = { groupId?: string; name?: string; slug?: string; + role?: string; + customRole?: { + name: string; + slug: string; + } } ) => void; }; @@ -46,41 +52,40 @@ type Props = { export const OrgGroupsTable = ({ handlePopUpOpen }: Props) => { - // const { createNotification } = useNotificationContext(); + const { createNotification } = useNotificationContext(); const [searchGroupsFilter, setSearchGroupsFilter] = useState(""); const { currentOrg } = useOrganization(); const orgId = currentOrg?.id || ""; const { isLoading, data: groups } = useGetOrganizationGroups(orgId); + const { mutateAsync: updateMutateAsync } = useUpdateGroup(); - // const { data: roles } = useGetOrgRoles(orgId); + const { data: roles } = useGetOrgRoles(orgId); - console.log("OrgGroupsTable groups: ", groups); - console.log("OrgGroupsTable roles: ", groups); - - // const handleChangeRole = ({ - // groupId, - // role - // }: { - // groupId: string; - // role: string; - // }) => { - // try { + const handleChangeRole = async ({ + currentSlug, + role + }: { + currentSlug: string; + role: string; + }) => { + try { + await updateMutateAsync({ + currentSlug, + role + }); - // // TODO - - // createNotification({ - // text: "Successfully updated group role", - // type: "success" - // }); - // } catch (err) { - // console.error(err); - - // createNotification({ - // text: "Failed to update group role", - // type: "error" - // }); - // } - // } + createNotification({ + text: "Successfully updated group role", + type: "success" + }); + } catch (err) { + console.error(err); + createNotification({ + text: "Failed to update group role", + type: "error" + }); + } + } return (
@@ -102,12 +107,40 @@ export const OrgGroupsTable = ({ {isLoading && } - {!isLoading && groups?.map(({ id, name, slug }) => { + {!isLoading && groups?.map(({ id, name, slug, role, customRole }) => { return ( {name} {slug} - N/A + + + {(isAllowed) => { + return ( + + ); + }} + +
{ - console.log("Delete group"); handlePopUpOpen("deleteGroup", { slug, name diff --git a/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityTable.tsx b/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityTable.tsx index 268092b44..9f58d7cdb 100644 --- a/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityTable.tsx +++ b/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityTable.tsx @@ -52,9 +52,6 @@ export const IdentityTable = ({ handlePopUpOpen }: Props) => { const { data, isLoading } = useGetIdentityMembershipOrgs(orgId); const { data: roles } = useGetOrgRoles(orgId); - - console.log("IdentityTable data: ", data); - console.log("IdentityTable roles: ", roles); const handleChangeRole = async ({ identityId, role }: { identityId: string; role: string }) => { try { diff --git a/frontend/src/views/Org/MembersPage/components/OrgMembersTab/components/OrgMembersSection/OrgMembersTable.tsx b/frontend/src/views/Org/MembersPage/components/OrgMembersTab/components/OrgMembersSection/OrgMembersTable.tsx index 322e457ae..3af435b4f 100644 --- a/frontend/src/views/Org/MembersPage/components/OrgMembersTab/components/OrgMembersSection/OrgMembersTable.tsx +++ b/frontend/src/views/Org/MembersPage/components/OrgMembersTab/components/OrgMembersSection/OrgMembersTable.tsx @@ -62,8 +62,6 @@ export const OrgMembersTable = ({ handlePopUpOpen, setCompleteInviteLink }: Prop const { data: serverDetails } = useFetchServerStatus(); const { data: members, isLoading: isMembersLoading } = useGetOrgUsers(orgId); - - console.log("OrgGroupsTable members: ", members); const { mutateAsync: addUserMutateAsync } = useAddUserToOrg(); const { mutateAsync: updateUserOrgRole } = useUpdateOrgUserRole();