import React, { useState } from "react"; import { Link, useLocation } from "react-router-dom"; import { useTranslation } from "react-i18next"; import _ from "lodash"; import { AlertVariant, Button, Checkbox, Dropdown, DropdownItem, KebabToggle, ToolbarItem, } from "@patternfly/react-core"; import type GroupRepresentation from "keycloak-admin/lib/defs/groupRepresentation"; import type UserRepresentation from "keycloak-admin/lib/defs/userRepresentation"; import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable"; import { useAdminClient } from "../context/auth/AdminClient"; import { useRealm } from "../context/realm-context/RealmContext"; import { useAlerts } from "../components/alert/Alerts"; import { emptyFormatter } from "../util"; import { getLastId } from "./groupIdUtils"; import { useSubGroups } from "./SubGroupsContext"; import { MemberModal } from "./MembersModal"; import { ListEmptyState } from "../components/list-empty-state/ListEmptyState"; import { GroupPath } from "../components/group/GroupPath"; import { toUser } from "../user/routes/User"; type MembersOf = UserRepresentation & { membership: GroupRepresentation[]; }; export const Members = () => { const { t } = useTranslation("groups"); const adminClient = useAdminClient(); const { realm } = useRealm(); const { addAlert, addError } = useAlerts(); const location = useLocation(); const id = getLastId(location.pathname); const [includeSubGroup, setIncludeSubGroup] = useState(false); const { currentGroup } = useSubGroups(); const [addMembers, setAddMembers] = useState(false); const [isKebabOpen, setIsKebabOpen] = useState(false); const [selectedRows, setSelectedRows] = useState([]); const [key, setKey] = useState(0); const refresh = () => setKey(new Date().getTime()); const getMembership = async (id: string) => await adminClient.users.listGroups({ id: id! }); const getSubGroups = (groups: GroupRepresentation[]) => { let subGroups: GroupRepresentation[] = []; for (const group of groups!) { subGroups.push(group); const subs = getSubGroups(group.subGroups!); subGroups = subGroups.concat(subs); } return subGroups; }; const loader = async (first?: number, max?: number) => { let members = await adminClient.groups.listMembers({ id: id!, first, max, }); if (includeSubGroup) { const subGroups = getSubGroups(currentGroup().subGroups!); for (const group of subGroups) { members = members.concat( await adminClient.groups.listMembers({ id: group.id! }) ); } members = _.uniqBy(members, (member) => member.username); } const memberOfPromises = await Promise.all( members.map((member) => getMembership(member.id!)) ); return members.map((member: UserRepresentation, i) => { return { ...member, membership: memberOfPromises[i] }; }); }; const MemberOfRenderer = (member: MembersOf) => { return ( <> {member.membership.map((group, index) => ( <> {member.membership[index + 1] ? ", " : ""} ))} ); }; const UserDetailLink = (user: MembersOf) => ( <> {user.username} ); return ( <> {addMembers && ( { setAddMembers(false); refresh(); }} /> )} setSelectedRows([...rows])} toolbarItem={ <> setIncludeSubGroup(!includeSubGroup)} /> setIsKebabOpen(!isKebabOpen)} isDisabled={selectedRows.length === 0} /> } isOpen={isKebabOpen} isPlain dropdownItems={[ { try { await Promise.all( selectedRows.map((user) => adminClient.users.delFromGroup({ id: user.id!, groupId: id!, }) ) ); setIsKebabOpen(false); addAlert( t("usersLeft", { count: selectedRows.length }), AlertVariant.success ); } catch (error) { addError("groups:usersLeftError", error); } refresh(); }} > {t("leave")} , ]} /> } actions={[ { title: t("leave"), onRowClick: async (user) => { try { await adminClient.users.delFromGroup({ id: user.id!, groupId: id!, }); addAlert(t("usersLeft", { count: 1 }), AlertVariant.success); } catch (error) { addError("groups:usersLeftError", error); } return true; }, }, ]} columns={[ { name: "username", displayKey: "common:name", cellRenderer: UserDetailLink, }, { name: "email", displayKey: "groups:email", cellFormatters: [emptyFormatter()], }, { name: "firstName", displayKey: "groups:firstName", cellFormatters: [emptyFormatter()], }, { name: "lastName", displayKey: "groups:lastName", cellFormatters: [emptyFormatter()], }, { name: "membership", displayKey: "groups:membership", cellRenderer: MemberOfRenderer, }, ]} emptyState={ setAddMembers(true)} /> } /> ); };