Users(groups): Add modal for joining user groups (#513)
* add cypress test to add groups to user * add test to leave group * format * update snapshots * add user to groups modal wip * wip join group functionality * add modal to add user groups * add refresh * remove comment * lint and format * fix empty state * add cypress test to add groups to user * format * revert snap * remove existing joined groups from modal
This commit is contained in:
parent
3deea60a87
commit
6f4ea86ecb
8 changed files with 381 additions and 16 deletions
|
@ -6,11 +6,44 @@ import ListingPage from "../support/pages/admin_console/ListingPage";
|
||||||
import UserDetailsPage from "../support/pages/admin_console/manage/users/UserDetailsPage";
|
import UserDetailsPage from "../support/pages/admin_console/manage/users/UserDetailsPage";
|
||||||
import ModalUtils from "../support/util/ModalUtils";
|
import ModalUtils from "../support/util/ModalUtils";
|
||||||
import { keycloakBefore } from "../support/util/keycloak_before";
|
import { keycloakBefore } from "../support/util/keycloak_before";
|
||||||
|
import GroupModal from "../support/pages/admin_console/manage/groups/GroupModal";
|
||||||
|
import UserGroupsPage from "../support/pages/admin_console/manage/users/UserGroupsPage";
|
||||||
|
|
||||||
|
let groupName = "group";
|
||||||
|
|
||||||
|
describe("Group creation", () => {
|
||||||
|
const loginPage = new LoginPage();
|
||||||
|
const masthead = new Masthead();
|
||||||
|
const sidebarPage = new SidebarPage();
|
||||||
|
const listingPage = new ListingPage();
|
||||||
|
const groupModal = new GroupModal();
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
keycloakBefore();
|
||||||
|
loginPage.logIn();
|
||||||
|
sidebarPage.goToGroups();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Add group to be joined", () => {
|
||||||
|
groupName += "_" + (Math.random() + 1).toString(36).substring(7);
|
||||||
|
|
||||||
|
groupModal
|
||||||
|
.open("openCreateGroupModal")
|
||||||
|
.fillGroupForm(groupName)
|
||||||
|
.clickCreate();
|
||||||
|
|
||||||
|
masthead.checkNotificationMessage("Group created");
|
||||||
|
|
||||||
|
sidebarPage.goToGroups();
|
||||||
|
listingPage.searchItem(groupName, false).itemExist(groupName);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("Users test", () => {
|
describe("Users test", () => {
|
||||||
const loginPage = new LoginPage();
|
const loginPage = new LoginPage();
|
||||||
const sidebarPage = new SidebarPage();
|
const sidebarPage = new SidebarPage();
|
||||||
const createUserPage = new CreateUserPage();
|
const createUserPage = new CreateUserPage();
|
||||||
|
const userGroupsPage = new UserGroupsPage();
|
||||||
const masthead = new Masthead();
|
const masthead = new Masthead();
|
||||||
const modalUtils = new ModalUtils();
|
const modalUtils = new ModalUtils();
|
||||||
const listingPage = new ListingPage();
|
const listingPage = new ListingPage();
|
||||||
|
@ -51,7 +84,7 @@ describe("Users test", () => {
|
||||||
sidebarPage.goToUsers();
|
sidebarPage.goToUsers();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Go to user details test", function () {
|
it("User details test", function () {
|
||||||
cy.wait(1000);
|
cy.wait(1000);
|
||||||
listingPage.searchItem(itemId).itemExist(itemId);
|
listingPage.searchItem(itemId).itemExist(itemId);
|
||||||
|
|
||||||
|
@ -64,13 +97,37 @@ describe("Users test", () => {
|
||||||
|
|
||||||
cy.wait(1000);
|
cy.wait(1000);
|
||||||
|
|
||||||
// Go to user details
|
|
||||||
|
|
||||||
cy.getId("user-groups-tab").click();
|
|
||||||
|
|
||||||
sidebarPage.goToUsers();
|
sidebarPage.goToUsers();
|
||||||
listingPage.searchItem(itemId).itemExist(itemId);
|
listingPage.searchItem(itemId).itemExist(itemId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Add user to group test", function () {
|
||||||
|
// Go to user groups
|
||||||
|
|
||||||
|
listingPage.searchItem(itemId).itemExist(itemId);
|
||||||
|
listingPage.goToItemDetails(itemId);
|
||||||
|
|
||||||
|
userGroupsPage.goToGroupsTab();
|
||||||
|
userGroupsPage.toggleAddGroupModal();
|
||||||
|
cy.getId(`${groupName}`).click();
|
||||||
|
userGroupsPage.joinGroup();
|
||||||
|
|
||||||
|
cy.wait(1000);
|
||||||
|
|
||||||
|
listingPage.itemExist(groupName);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Leave group test", function () {
|
||||||
|
listingPage.searchItem(itemId).itemExist(itemId);
|
||||||
|
listingPage.goToItemDetails(itemId);
|
||||||
|
// Go to user groups
|
||||||
|
userGroupsPage.goToGroupsTab();
|
||||||
|
cy.getId(`leave-${groupName}`).click();
|
||||||
|
cy.getId("modalConfirm").click();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Delete user", function () {
|
||||||
|
listingPage.searchItem(itemId).itemExist(itemId);
|
||||||
// Delete
|
// Delete
|
||||||
cy.wait(1000);
|
cy.wait(1000);
|
||||||
listingPage.deleteItem(itemId);
|
listingPage.deleteItem(itemId);
|
||||||
|
@ -81,6 +138,5 @@ describe("Users test", () => {
|
||||||
|
|
||||||
listingPage.itemExist(itemId, false);
|
listingPage.itemExist(itemId, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -0,0 +1,29 @@
|
||||||
|
export default class UserGroupsPage {
|
||||||
|
userGroupsTab: string;
|
||||||
|
addGroupButton: string;
|
||||||
|
joinGroupButton: string;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.userGroupsTab = "user-groups-tab";
|
||||||
|
this.addGroupButton = "add-group-button";
|
||||||
|
this.joinGroupButton = "joinGroup";
|
||||||
|
}
|
||||||
|
|
||||||
|
goToGroupsTab() {
|
||||||
|
cy.getId(this.userGroupsTab).click();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleAddGroupModal() {
|
||||||
|
cy.getId(this.addGroupButton).click();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
joinGroup() {
|
||||||
|
cy.getId(this.joinGroupButton).click();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -67,6 +67,7 @@ export const ConfirmDialogModal = ({
|
||||||
actions={[
|
actions={[
|
||||||
<Button
|
<Button
|
||||||
id="modal-confirm"
|
id="modal-confirm"
|
||||||
|
data-testid="modalConfirm"
|
||||||
key="confirm"
|
key="confirm"
|
||||||
variant={continueButtonVariant || ButtonVariant.primary}
|
variant={continueButtonVariant || ButtonVariant.primary}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|
|
@ -22,6 +22,7 @@ exports[`Confirmation dialog renders simple confirm dialog 1`] = `
|
||||||
actions={
|
actions={
|
||||||
Array [
|
Array [
|
||||||
<Button
|
<Button
|
||||||
|
data-testid="modalConfirm"
|
||||||
id="modal-confirm"
|
id="modal-confirm"
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
variant="primary"
|
variant="primary"
|
||||||
|
@ -125,6 +126,7 @@ exports[`Confirmation dialog renders simple confirm dialog 1`] = `
|
||||||
data-ouia-component-id="OUIA-Generated-Button-primary-1"
|
data-ouia-component-id="OUIA-Generated-Button-primary-1"
|
||||||
data-ouia-component-type="PF4/Button"
|
data-ouia-component-type="PF4/Button"
|
||||||
data-ouia-safe="true"
|
data-ouia-safe="true"
|
||||||
|
data-testid="modalConfirm"
|
||||||
id="modal-confirm"
|
id="modal-confirm"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
|
@ -152,6 +154,7 @@ exports[`Confirmation dialog renders simple confirm dialog 1`] = `
|
||||||
actions={
|
actions={
|
||||||
Array [
|
Array [
|
||||||
<Button
|
<Button
|
||||||
|
data-testid="modalConfirm"
|
||||||
id="modal-confirm"
|
id="modal-confirm"
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
variant="primary"
|
variant="primary"
|
||||||
|
@ -316,6 +319,7 @@ exports[`Confirmation dialog renders simple confirm dialog 1`] = `
|
||||||
className="pf-c-modal-box__footer"
|
className="pf-c-modal-box__footer"
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
|
data-testid="modalConfirm"
|
||||||
id="modal-confirm"
|
id="modal-confirm"
|
||||||
key="confirm"
|
key="confirm"
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
|
@ -328,6 +332,7 @@ exports[`Confirmation dialog renders simple confirm dialog 1`] = `
|
||||||
data-ouia-component-id="OUIA-Generated-Button-primary-1"
|
data-ouia-component-id="OUIA-Generated-Button-primary-1"
|
||||||
data-ouia-component-type="PF4/Button"
|
data-ouia-component-type="PF4/Button"
|
||||||
data-ouia-safe={true}
|
data-ouia-safe={true}
|
||||||
|
data-testid="modalConfirm"
|
||||||
disabled={false}
|
disabled={false}
|
||||||
id="modal-confirm"
|
id="modal-confirm"
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
|
|
|
@ -81,7 +81,7 @@ export const MoveGroupDialog = ({
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
actions={[
|
actions={[
|
||||||
<Button
|
<Button
|
||||||
data-testid="moveGroup"
|
data-testid="joinGroup"
|
||||||
key="confirm"
|
key="confirm"
|
||||||
variant="primary"
|
variant="primary"
|
||||||
form="group-form"
|
form="group-form"
|
||||||
|
|
203
src/user/JoinGroupDialog.tsx
Normal file
203
src/user/JoinGroupDialog.tsx
Normal file
|
@ -0,0 +1,203 @@
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import {
|
||||||
|
Breadcrumb,
|
||||||
|
BreadcrumbItem,
|
||||||
|
Button,
|
||||||
|
ButtonVariant,
|
||||||
|
DataList,
|
||||||
|
DataListAction,
|
||||||
|
DataListCell,
|
||||||
|
DataListItem,
|
||||||
|
DataListItemCells,
|
||||||
|
DataListItemRow,
|
||||||
|
InputGroup,
|
||||||
|
Modal,
|
||||||
|
ModalVariant,
|
||||||
|
TextInput,
|
||||||
|
Toolbar,
|
||||||
|
ToolbarContent,
|
||||||
|
ToolbarItem,
|
||||||
|
} from "@patternfly/react-core";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { asyncStateFetch, useAdminClient } from "../context/auth/AdminClient";
|
||||||
|
import { AngleRightIcon, SearchIcon } from "@patternfly/react-icons";
|
||||||
|
import GroupRepresentation from "keycloak-admin/lib/defs/groupRepresentation";
|
||||||
|
import { useErrorHandler } from "react-error-boundary";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
import _ from "lodash";
|
||||||
|
export type JoinGroupDialogProps = {
|
||||||
|
open: boolean;
|
||||||
|
toggleDialog: () => void;
|
||||||
|
onClose: () => void;
|
||||||
|
onConfirm: (newGroup: GroupRepresentation) => void;
|
||||||
|
username: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const JoinGroupDialog = ({
|
||||||
|
onClose,
|
||||||
|
open,
|
||||||
|
toggleDialog,
|
||||||
|
onConfirm,
|
||||||
|
username,
|
||||||
|
}: JoinGroupDialogProps) => {
|
||||||
|
const { t } = useTranslation("roles");
|
||||||
|
const adminClient = useAdminClient();
|
||||||
|
|
||||||
|
const errorHandler = useErrorHandler();
|
||||||
|
|
||||||
|
const [navigation, setNavigation] = useState<GroupRepresentation[]>([]);
|
||||||
|
const [groups, setGroups] = useState<GroupRepresentation[]>([]);
|
||||||
|
const [filtered, setFiltered] = useState<GroupRepresentation[]>();
|
||||||
|
const [filter, setFilter] = useState("");
|
||||||
|
|
||||||
|
const [groupId, setGroupId] = useState<string>();
|
||||||
|
|
||||||
|
const { id } = useParams<{ id: string }>();
|
||||||
|
|
||||||
|
useEffect(
|
||||||
|
() =>
|
||||||
|
asyncStateFetch(
|
||||||
|
async () => {
|
||||||
|
const existingUserGroups = await adminClient.users.listGroups({ id });
|
||||||
|
const allGroups = await adminClient.groups.find();
|
||||||
|
|
||||||
|
if (groupId) {
|
||||||
|
const group = await adminClient.groups.findOne({ id: groupId });
|
||||||
|
return { group, groups: group.subGroups! };
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
groups: _.differenceBy(allGroups, existingUserGroups, "id"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async ({ group: selectedGroup, groups }) => {
|
||||||
|
if (selectedGroup) {
|
||||||
|
setNavigation([...navigation, selectedGroup]);
|
||||||
|
}
|
||||||
|
|
||||||
|
setGroups(groups);
|
||||||
|
},
|
||||||
|
errorHandler
|
||||||
|
),
|
||||||
|
[groupId]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
variant={ModalVariant.small}
|
||||||
|
title={`Join groups for user ${username}`}
|
||||||
|
isOpen={open}
|
||||||
|
onClose={onClose}
|
||||||
|
actions={[
|
||||||
|
<Button
|
||||||
|
data-testid="joinGroup"
|
||||||
|
key="confirm"
|
||||||
|
variant="primary"
|
||||||
|
form="group-form"
|
||||||
|
onClick={() => {
|
||||||
|
toggleDialog();
|
||||||
|
onConfirm(navigation[navigation.length - 1]);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("users:Join")}
|
||||||
|
</Button>,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Breadcrumb>
|
||||||
|
<BreadcrumbItem key="home">
|
||||||
|
<Button
|
||||||
|
variant="link"
|
||||||
|
onClick={() => {
|
||||||
|
setGroupId(undefined);
|
||||||
|
setNavigation([]);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("groups")}
|
||||||
|
</Button>
|
||||||
|
</BreadcrumbItem>
|
||||||
|
{navigation.map((group, i) => (
|
||||||
|
<BreadcrumbItem key={i}>
|
||||||
|
{navigation.length - 1 !== i && (
|
||||||
|
<Button
|
||||||
|
variant="link"
|
||||||
|
onClick={() => {
|
||||||
|
setGroupId(group.id);
|
||||||
|
setNavigation([...navigation].slice(0, i));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{group.name}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{navigation.length - 1 === i && <>{group.name}</>}
|
||||||
|
</BreadcrumbItem>
|
||||||
|
))}
|
||||||
|
</Breadcrumb>
|
||||||
|
|
||||||
|
<Toolbar>
|
||||||
|
<ToolbarContent>
|
||||||
|
<ToolbarItem>
|
||||||
|
<InputGroup>
|
||||||
|
<TextInput
|
||||||
|
type="search"
|
||||||
|
aria-label={t("common:search")}
|
||||||
|
placeholder={t("users:searchForGroups")}
|
||||||
|
onChange={(value) => {
|
||||||
|
if (value === "") {
|
||||||
|
setFiltered(undefined);
|
||||||
|
}
|
||||||
|
setFilter(value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
variant={ButtonVariant.control}
|
||||||
|
aria-label={t("common:search")}
|
||||||
|
onClick={() =>
|
||||||
|
setFiltered(
|
||||||
|
groups.filter((group) =>
|
||||||
|
group.name?.toLowerCase().includes(filter.toLowerCase())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SearchIcon />
|
||||||
|
</Button>
|
||||||
|
</InputGroup>
|
||||||
|
</ToolbarItem>
|
||||||
|
</ToolbarContent>
|
||||||
|
</Toolbar>
|
||||||
|
<DataList
|
||||||
|
onSelectDataListItem={(value) => setGroupId(value)}
|
||||||
|
aria-label={t("groups")}
|
||||||
|
isCompact
|
||||||
|
>
|
||||||
|
{(filtered || groups).map((group) => (
|
||||||
|
<DataListItem
|
||||||
|
aria-labelledby={group.name}
|
||||||
|
key={group.id}
|
||||||
|
id={group.id}
|
||||||
|
>
|
||||||
|
<DataListItemRow data-testid={group.name}>
|
||||||
|
<DataListItemCells
|
||||||
|
dataListCells={[
|
||||||
|
<DataListCell key={`name-${group.id}`}>
|
||||||
|
<>{group.name}</>
|
||||||
|
</DataListCell>,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<DataListAction
|
||||||
|
aria-labelledby={`select-${group.name}`}
|
||||||
|
id={`select-${group.name}`}
|
||||||
|
aria-label={t("groupName")}
|
||||||
|
isPlainButtonAction
|
||||||
|
>
|
||||||
|
<Button isDisabled variant="link">
|
||||||
|
<AngleRightIcon />
|
||||||
|
</Button>
|
||||||
|
</DataListAction>
|
||||||
|
</DataListItemRow>
|
||||||
|
</DataListItem>
|
||||||
|
))}
|
||||||
|
</DataList>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
|
@ -18,6 +18,22 @@ import GroupRepresentation from "keycloak-admin/lib/defs/groupRepresentation";
|
||||||
import { cellWidth } from "@patternfly/react-table";
|
import { cellWidth } from "@patternfly/react-table";
|
||||||
import { useErrorHandler } from "react-error-boundary";
|
import { useErrorHandler } from "react-error-boundary";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
import UserRepresentation from "keycloak-admin/lib/defs/userRepresentation";
|
||||||
|
import { JoinGroupDialog } from "./JoinGroupDialog";
|
||||||
|
|
||||||
|
type GroupTableData = GroupRepresentation & {
|
||||||
|
membersLength?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UserFormProps = {
|
||||||
|
username?: string;
|
||||||
|
loader?: (
|
||||||
|
first?: number,
|
||||||
|
max?: number,
|
||||||
|
search?: string
|
||||||
|
) => Promise<UserRepresentation[]>;
|
||||||
|
addGroup?: (newGroup: GroupRepresentation) => void;
|
||||||
|
};
|
||||||
|
|
||||||
export const UserGroups = () => {
|
export const UserGroups = () => {
|
||||||
const { t } = useTranslation("roles");
|
const { t } = useTranslation("roles");
|
||||||
|
@ -27,7 +43,9 @@ export const UserGroups = () => {
|
||||||
const handleError = useErrorHandler();
|
const handleError = useErrorHandler();
|
||||||
|
|
||||||
const [selectedGroup, setSelectedGroup] = useState<GroupRepresentation>();
|
const [selectedGroup, setSelectedGroup] = useState<GroupRepresentation>();
|
||||||
|
const [list, setList] = useState(false);
|
||||||
const [listGroups, setListGroups] = useState(true);
|
const [listGroups, setListGroups] = useState(true);
|
||||||
|
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
const [username, setUsername] = useState("");
|
const [username, setUsername] = useState("");
|
||||||
|
|
||||||
|
@ -58,7 +76,7 @@ export const UserGroups = () => {
|
||||||
setSearch(searchParam);
|
setSearch(searchParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!searchParam && !listGroups) {
|
if (!searchParam && !listGroups && !list) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,6 +159,7 @@ export const UserGroups = () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
setDirectMembershipList(directMembership);
|
setDirectMembershipList(directMembership);
|
||||||
|
|
||||||
const filterDupesfromGroups = allPaths.filter(
|
const filterDupesfromGroups = allPaths.filter(
|
||||||
(thing, index, self) =>
|
(thing, index, self) =>
|
||||||
index === self.findIndex((t) => t.name === thing.name)
|
index === self.findIndex((t) => t.name === thing.name)
|
||||||
|
@ -163,7 +182,7 @@ export const UserGroups = () => {
|
||||||
},
|
},
|
||||||
handleError
|
handleError
|
||||||
);
|
);
|
||||||
});
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
refresh();
|
refresh();
|
||||||
|
@ -173,7 +192,24 @@ export const UserGroups = () => {
|
||||||
return <>{group.name}</>;
|
return <>{group.name}</>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleModal = () => setOpen(!open);
|
const JoinGroupButtonRenderer = (group: GroupRepresentation) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Button onClick={() => joinGroup(group)} variant="link">
|
||||||
|
{t("users:joinGroup")}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleModal = () => {
|
||||||
|
setOpen(!open);
|
||||||
|
};
|
||||||
|
|
||||||
|
const joinGroup = (group: GroupRepresentation) => {
|
||||||
|
setSelectedGroup(group);
|
||||||
|
toggleModal();
|
||||||
|
};
|
||||||
|
|
||||||
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
|
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
|
||||||
titleKey: t("users:leaveGroup", {
|
titleKey: t("users:leaveGroup", {
|
||||||
|
@ -210,11 +246,16 @@ export const UserGroups = () => {
|
||||||
const LeaveButtonRenderer = (group: GroupRepresentation) => {
|
const LeaveButtonRenderer = (group: GroupRepresentation) => {
|
||||||
if (
|
if (
|
||||||
directMembershipList.some((item) => item.id === group.id) ||
|
directMembershipList.some((item) => item.id === group.id) ||
|
||||||
directMembershipList.length === 0
|
directMembershipList.length === 0 ||
|
||||||
|
isDirectMembership
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button onClick={() => leave(group)} variant="link">
|
<Button
|
||||||
|
data-testid={`leave-${group.name}`}
|
||||||
|
onClick={() => leave(group)}
|
||||||
|
variant="link"
|
||||||
|
>
|
||||||
{t("users:Leave")}
|
{t("users:Leave")}
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
|
@ -224,10 +265,38 @@ export const UserGroups = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const addGroup = async (group: GroupRepresentation): Promise<void> => {
|
||||||
|
const newGroup = group;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await adminClient.users.addToGroup({
|
||||||
|
id: id,
|
||||||
|
groupId: newGroup.id!,
|
||||||
|
});
|
||||||
|
setList(true);
|
||||||
|
refresh();
|
||||||
|
addAlert(t("users:addedGroupMembership"), AlertVariant.success);
|
||||||
|
} catch (error) {
|
||||||
|
addAlert(
|
||||||
|
t("users:addedGroupMembershipError", { error }),
|
||||||
|
AlertVariant.danger
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageSection variant="light">
|
<PageSection variant="light">
|
||||||
<DeleteConfirm />
|
<DeleteConfirm />
|
||||||
|
{open && (
|
||||||
|
<JoinGroupDialog
|
||||||
|
open={open}
|
||||||
|
onClose={() => setOpen(!open)}
|
||||||
|
onConfirm={addGroup}
|
||||||
|
toggleDialog={() => toggleModal()}
|
||||||
|
username={username}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<KeycloakDataTable
|
<KeycloakDataTable
|
||||||
key={key}
|
key={key}
|
||||||
loader={loader}
|
loader={loader}
|
||||||
|
@ -241,11 +310,12 @@ export const UserGroups = () => {
|
||||||
<Button
|
<Button
|
||||||
className="kc-join-group-button"
|
className="kc-join-group-button"
|
||||||
key="join-group-button"
|
key="join-group-button"
|
||||||
onClick={() => toggleModal()}
|
onClick={toggleModal}
|
||||||
data-testid="add-group-button"
|
data-testid="add-group-button"
|
||||||
>
|
>
|
||||||
{t("users:joinGroup")}
|
{t("users:joinGroup")}
|
||||||
</Button>
|
</Button>
|
||||||
|
{JoinGroupButtonRenderer}
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={t("users:directMembership")}
|
label={t("users:directMembership")}
|
||||||
key="direct-membership-check"
|
key="direct-membership-check"
|
||||||
|
@ -283,8 +353,6 @@ export const UserGroups = () => {
|
||||||
hasIcon={true}
|
hasIcon={true}
|
||||||
message={t("users:noGroups")}
|
message={t("users:noGroups")}
|
||||||
instructions={t("users:noGroupsText")}
|
instructions={t("users:noGroupsText")}
|
||||||
primaryActionText={t("users:joinGroup")}
|
|
||||||
onPrimaryAction={() => {}}
|
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
""
|
""
|
||||||
|
|
|
@ -10,11 +10,14 @@
|
||||||
"noGroups": "No groups",
|
"noGroups": "No groups",
|
||||||
"noGroupsText": "You haven't added this user to any groups. Join a group to get started.",
|
"noGroupsText": "You haven't added this user to any groups. Join a group to get started.",
|
||||||
"joinGroup": "Join Group",
|
"joinGroup": "Join Group",
|
||||||
|
"searchForGroups": "Search for groups",
|
||||||
"leave": "Leave",
|
"leave": "Leave",
|
||||||
"leaveGroup": "Leave group {{name}}?",
|
"leaveGroup": "Leave group {{name}}?",
|
||||||
"leaveGroupConfirmDialog": "Are you sure you want to remove {{username}} from the group {{groupname}}?",
|
"leaveGroupConfirmDialog": "Are you sure you want to remove {{username}} from the group {{groupname}}?",
|
||||||
"directMembership": "Direct membership",
|
"directMembership": "Direct membership",
|
||||||
"groupMembership": "Group membership",
|
"groupMembership": "Group membership",
|
||||||
|
"addedGroupMembership": "Added group membership",
|
||||||
|
"addedGroupMembershipError": "Error adding group membership",
|
||||||
"removedGroupMembership": "Removed group membership",
|
"removedGroupMembership": "Removed group membership",
|
||||||
"removedGroupMembershipError": "Error removing group membership",
|
"removedGroupMembershipError": "Error removing group membership",
|
||||||
"path": "Path",
|
"path": "Path",
|
||||||
|
|
Loading…
Reference in a new issue