/* 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 && (
}>
{t("disabled")}
)}
);
};
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),
},
]
: []
}
/>
}
/>
>
);
};