/* eslint-disable */ // @ts-nocheck import type GroupRepresentation from "@keycloak/keycloak-admin-client/lib/defs/groupRepresentation"; import type UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userRepresentation"; import { SubGroupQuery } from "@keycloak/keycloak-admin-client/lib/resources/groups"; import { Action, KeycloakDataTable, ListEmptyState, useAlerts, useFetch, } from "../../shared/keycloak-ui-shared"; import { Button, Checkbox, Dropdown, DropdownItem, DropdownList, Label, MenuToggle, ToolbarItem, } from "../../shared/@patternfly/react-core"; import { EllipsisVIcon, InfoCircleIcon } from "../../shared/@patternfly/react-icons"; import { uniqBy } from "lodash-es"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import { Link, useLocation } from "react-router-dom"; import { useAdminClient } from "../admin-client"; import { KeycloakSpinner } from "../../shared/keycloak-ui-shared"; import { useAccess } from "../context/access/Access"; import { useRealm } from "../context/realm-context/RealmContext"; import { toUser } from "../user/routes/User"; import { emptyFormatter } from "../util"; import { MemberModal } from "./MembersModal"; import { useSubGroups } from "./SubGroupsContext"; import { getLastId } from "./groupIdUtils"; import { MembershipsModal } from "./MembershipsModal"; import useToggle from "../utils/useToggle"; import { useGroupResource } from "../context/group-resource/GroupResourceContext"; const UserDetailLink = (user: UserRepresentation) => { const { realm } = useRealm(); const { t } = useTranslation(); return ( {user.username}{" "} {!user.enabled && ( )} ); }; export const Members = () => { const { adminClient } = useAdminClient(); const groups = useGroupResource(); const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); const location = useLocation(); const id = getLastId(location.pathname); const [includeSubGroup, setIncludeSubGroup] = useState(false); const { currentGroup: group } = useSubGroups(); const [currentGroup, setCurrentGroup] = useState(); const [addMembers, setAddMembers] = useState(false); const [isKebabOpen, setIsKebabOpen] = useState(false); const [selectedRows, setSelectedRows] = useState([]); const [selectedUser, setSelectedUser] = useState(); const [showMemberships, toggleShowMemberships] = useToggle(); const { hasAccess } = useAccess(); useFetch(() => groups.findOne({ id: group()!.id! }), setCurrentGroup, []); const isManager = hasAccess("manage-users") || currentGroup?.access!.manageMembership; const [key, setKey] = useState(0); const refresh = () => setKey(new Date().getTime()); // this queries the subgroups using the new search paradigm but doesn't // account for pagination and therefore isn't going to scale well const getSubGroups = async (groupId?: string, count = 0) => { let nestedGroups: GroupRepresentation[] = []; if (!count || !groupId) { return nestedGroups; } const args: SubGroupQuery = { parentId: groupId, first: 0, max: count, }; const subGroups: GroupRepresentation[] = await groups.listSubGroups(args); nestedGroups = nestedGroups.concat(subGroups); await Promise.all( subGroups.map((g) => getSubGroups(g.id, g.subGroupCount)), ).then((values: GroupRepresentation[][]) => { values.forEach((groups) => (nestedGroups = nestedGroups.concat(groups))); }); return nestedGroups; }; const loader = async (first?: number, max?: number) => { if (!id) { return []; } let members = await groups.listMembers({ id: id!, briefRepresentation: true, first, max, }); if (includeSubGroup && currentGroup?.subGroupCount && currentGroup.id) { const subGroups = await getSubGroups( currentGroup.id, currentGroup.subGroupCount, ); await Promise.all( subGroups.map((g) => groups.listMembers({ id: g.id!, briefRepresentation: true, }), ), ).then((values: UserRepresentation[][]) => { values.forEach((users) => (members = members.concat(users))); }); members = uniqBy(members, (member) => member.username); } return members; }; if (!currentGroup) { return ; } return ( <> {addMembers && ( groups.listMembers({ id: id!, first, max }) } orgId={groups.getOrgId()} onAdd={async (selectedRows) => { try { await Promise.all( selectedRows.map(async (user) => { if (!groups.isOrgGroups()) { await adminClient.users.addToGroup({ id: user.id!, groupId: id!, }); } else { await groups.addMemberToOrgGroup({ groupId: id!, userId: user.id!, }); } }), ); addAlert(t("usersAdded", { count: selectedRows.length })); } catch (error) { addError("usersAddedError", error); } }} onClose={() => { setAddMembers(false); refresh(); }} /> )} {showMemberships && ( { toggleShowMemberships(); }} user={selectedUser!} orgId={groups.getOrgId()} /> )} setSelectedRows([...rows])} toolbarItem={ isManager && ( <> {!groups.isOrgGroups() && ( setIncludeSubGroup(!includeSubGroup)} /> )} setIsKebabOpen(isOpen)} toggle={(ref) => ( setIsKebabOpen(!isKebabOpen)} isExpanded={isKebabOpen} isDisabled={selectedRows.length === 0} aria-label="Actions" > )} shouldFocusToggleOnSelect isOpen={isKebabOpen} > { try { await Promise.all( selectedRows.map(async (user) => { if (!groups.isOrgGroups()) { await adminClient.users.delFromGroup({ id: user.id!, groupId: id!, }); } else { await groups.removeMemberFromOrgGroup({ groupId: id!, userId: user.id!, }); } }), ); setIsKebabOpen(false); addAlert( t("usersLeft", { count: selectedRows.length }), ); } catch (error) { addError("usersLeftError", error); } refresh(); }} > {t("leave")} ) } actions={[ ...(isManager ? [ { title: t("leave"), onRowClick: async (user) => { try { if (!groups.isOrgGroups()) { await adminClient.users.delFromGroup({ id: user.id!, groupId: id!, }); } else { await groups.removeMemberFromOrgGroup({ groupId: id!, userId: user.id!, }); } addAlert(t("usersLeft", { count: 1 })); } catch (error) { addError("usersLeftError", error); } return true; }, } as Action, ] : []), { title: t("showMemberships"), onRowClick: (user) => { setSelectedUser(user); toggleShowMemberships(); }, } as Action, ]} columns={[ { name: "username", displayKey: "name", cellRenderer: UserDetailLink, }, { name: "email", displayKey: "email", cellFormatters: [emptyFormatter()], }, { name: "firstName", displayKey: "firstName", cellFormatters: [emptyFormatter()], }, { name: "lastName", displayKey: "lastName", cellFormatters: [emptyFormatter()], }, ]} emptyState={ setAddMembers(true)} secondaryActions={ !groups.isOrgGroups() ? [ { text: t("includeSubGroups"), onClick: () => setIncludeSubGroup(true), }, ] : [] } /> } /> ); };