@keycloakify/keycloak-admin-ui
Version:
Repackaged Keycloak Admin UI
155 lines • 9.01 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { KeycloakDataTable, ListEmptyState, useAlerts, useFetch, } from "../ui-shared";
import { Button, Checkbox, Dropdown, DropdownItem, DropdownList, Label, MenuToggle, ToolbarItem, } from "@patternfly/react-core";
import { EllipsisVIcon, InfoCircleIcon } from "@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 "../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 "../groups/MembersModal";
import { useSubGroups } from "../groups/SubGroupsContext";
import { getLastId } from "../groups/groupIdUtils";
const UserDetailLink = (user) => {
const { realm } = useRealm();
const { t } = useTranslation();
return (_jsxs(Link, { to: toUser({ realm, id: user.id, tab: "settings" }), children: [user.username, " ", !user.enabled && (_jsx(Label, { color: "red", icon: _jsx(InfoCircleIcon, {}), children: t("disabled") }))] }, user.id));
};
export const Members = () => {
const { adminClient } = useAdminClient();
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 { hasAccess } = useAccess();
useFetch(() => adminClient.groups.findOne({ id: group().id }), setCurrentGroup, []);
const isManager = hasAccess("manage-users") || (currentGroup === null || currentGroup === void 0 ? void 0 : 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, count = 0) => {
let nestedGroups = [];
if (!count || !groupId) {
return nestedGroups;
}
const args = {
parentId: groupId,
first: 0,
max: count,
};
const subGroups = await adminClient.groups.listSubGroups(args);
nestedGroups = nestedGroups.concat(subGroups);
await Promise.all(subGroups.map((g) => getSubGroups(g.id, g.subGroupCount))).then((values) => {
values.forEach((groups) => (nestedGroups = nestedGroups.concat(groups)));
});
return nestedGroups;
};
const loader = async (first, max) => {
if (!id) {
return [];
}
let members = await adminClient.groups.listMembers({
id: id,
briefRepresentation: true,
first,
max,
});
if (includeSubGroup && (currentGroup === null || currentGroup === void 0 ? void 0 : currentGroup.subGroupCount) && currentGroup.id) {
const subGroups = await getSubGroups(currentGroup.id, currentGroup.subGroupCount);
await Promise.all(subGroups.map((g) => adminClient.groups.listMembers({
id: g.id,
briefRepresentation: true,
}))).then((values) => {
values.forEach((users) => (members = members.concat(users)));
});
members = uniqBy(members, (member) => member.username);
}
return members;
};
if (!currentGroup) {
return _jsx(KeycloakSpinner, {});
}
return (_jsxs(_Fragment, { children: [addMembers && (_jsx(MemberModal, { membersQuery: (first, max) => adminClient.groups.listMembers({ id: id, first, max }), onAdd: async (selectedRows) => {
try {
await Promise.all(selectedRows.map((user) => adminClient.users.addToGroup({ id: user.id, groupId: id })));
addAlert(t("usersAdded", { count: selectedRows.length }));
}
catch (error) {
addError("usersAddedError", error);
}
}, onClose: () => {
setAddMembers(false);
refresh();
} })), _jsx(KeycloakDataTable, { "data-testid": "members-table", loader: loader, ariaLabelKey: "members", isPaginated: true, canSelectAll: true, onSelect: (rows) => setSelectedRows([...rows]), toolbarItem: isManager && (_jsxs(_Fragment, { children: [_jsx(ToolbarItem, { children: _jsx(Button, { "data-testid": "addMember", variant: "primary", onClick: () => setAddMembers(true), children: t("addMember") }) }), _jsx(ToolbarItem, { children: _jsx(Checkbox, { "data-testid": "includeSubGroupsCheck", label: t("includeSubGroups"), id: "kc-include-sub-groups", isChecked: includeSubGroup, onChange: () => setIncludeSubGroup(!includeSubGroup) }) }), _jsx(ToolbarItem, { children: _jsx(Dropdown, { onOpenChange: (isOpen) => setIsKebabOpen(isOpen), toggle: (ref) => (_jsx(MenuToggle, { ref: ref, variant: "plain", onClick: () => setIsKebabOpen(!isKebabOpen), isExpanded: isKebabOpen, isDisabled: selectedRows.length === 0, "aria-label": "Actions", children: _jsx(EllipsisVIcon, {}) })), shouldFocusToggleOnSelect: true, isOpen: isKebabOpen, children: _jsx(DropdownList, { children: _jsx(DropdownItem, { component: "button", onClick: async () => {
try {
await Promise.all(selectedRows.map((user) => adminClient.users.delFromGroup({
id: user.id,
groupId: id,
})));
setIsKebabOpen(false);
addAlert(t("usersLeft", { count: selectedRows.length }));
}
catch (error) {
addError("usersLeftError", error);
}
refresh();
}, children: t("leave") }, "action") }) }) })] })), actions: isManager
? [
{
title: t("leave"),
onRowClick: async (user) => {
try {
await adminClient.users.delFromGroup({
id: user.id,
groupId: id,
});
addAlert(t("usersLeft", { count: 1 }));
}
catch (error) {
addError("usersLeftError", error);
}
return true;
},
},
]
: [], 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: _jsx(ListEmptyState, { message: t("noUsersFound"), instructions: isManager ? t("emptyInstructions") : undefined, primaryActionText: isManager ? t("addMember") : undefined, onPrimaryAction: () => setAddMembers(true), secondaryActions: [
{
text: t("includeSubGroups"),
onClick: () => setIncludeSubGroup(true),
},
] }) }, `${id}${key}${includeSubGroup}`)] }));
};
//# sourceMappingURL=Members.js.map