UNPKG

@keycloakify/keycloak-admin-ui

Version:
155 lines 9.01 kB
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