From d8c491123a1b6adf15224d36b1d357b0658125a9 Mon Sep 17 00:00:00 2001 From: jenny-s51 Date: Mon, 5 Apr 2021 16:29:03 -0400 Subject: [PATCH 1/9] add user to groups modal wip --- src/user/UserGroups.tsx | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/user/UserGroups.tsx b/src/user/UserGroups.tsx index ce353e47d5..c9d214f102 100644 --- a/src/user/UserGroups.tsx +++ b/src/user/UserGroups.tsx @@ -32,7 +32,7 @@ export type UserFormProps = { max?: number, search?: string ) => Promise; - addGroup?: (newGroup: GroupRepresentation) => void; + addGroups?: (newReps: GroupRepresentation[]) => void; }; export const UserGroups = () => { @@ -48,12 +48,17 @@ export const UserGroups = () => { const [search, setSearch] = useState(""); const [username, setUsername] = useState(""); + const [join, setJoin] = useState(); + + const lastId = getLastId(location.pathname); + const [isDirectMembership, setDirectMembership] = useState(true); const [directMembershipList, setDirectMembershipList] = useState< GroupRepresentation[] >([]); const [open, setOpen] = useState(false); + const [move, setMove] = useState(); const adminClient = useAdminClient(); const { id } = useParams<{ id: string }>(); @@ -205,6 +210,15 @@ export const UserGroups = () => { const toggleModal = () => { setOpen(!open); }; + const JoinGroupButtonRenderer = (group: GroupRepresentation) => { + return ( + <> + + + ); + }; const joinGroup = (group: GroupRepresentation) => { setSelectedGroup(group); From 0b106cb55929915dae1ed491e11bfaabc364c52b Mon Sep 17 00:00:00 2001 From: jenny-s51 Date: Tue, 6 Apr 2021 15:42:51 -0400 Subject: [PATCH 2/9] add logic for selecting multiple groups, API does not support this yet --- src/user/JoinGroupDialog.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/user/JoinGroupDialog.tsx b/src/user/JoinGroupDialog.tsx index 2802acd5bd..aaffe640db 100644 --- a/src/user/JoinGroupDialog.tsx +++ b/src/user/JoinGroupDialog.tsx @@ -39,6 +39,10 @@ type Group = GroupRepresentation & { checked?: boolean; }; +type Group = GroupRepresentation & { + checked?: boolean; +}; + export const JoinGroupDialog = ({ onClose, open, From 559bf1cceee63846e3fd902e7fa629d64778b051 Mon Sep 17 00:00:00 2001 From: jenny-s51 Date: Thu, 15 Apr 2021 11:32:19 -0400 Subject: [PATCH 3/9] cfixcheckbox select bug --- src/user/JoinGroupDialog.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/user/JoinGroupDialog.tsx b/src/user/JoinGroupDialog.tsx index aaffe640db..2802acd5bd 100644 --- a/src/user/JoinGroupDialog.tsx +++ b/src/user/JoinGroupDialog.tsx @@ -39,10 +39,6 @@ type Group = GroupRepresentation & { checked?: boolean; }; -type Group = GroupRepresentation & { - checked?: boolean; -}; - export const JoinGroupDialog = ({ onClose, open, From 62b21dbfe12fde5cfd8bd4ab9fdc5fa6866bd45e Mon Sep 17 00:00:00 2001 From: jenny-s51 Date: Thu, 15 Apr 2021 15:46:47 -0400 Subject: [PATCH 4/9] add functionality to select and add multiple groups --- src/user/UserGroups.tsx | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/user/UserGroups.tsx b/src/user/UserGroups.tsx index c9d214f102..ce353e47d5 100644 --- a/src/user/UserGroups.tsx +++ b/src/user/UserGroups.tsx @@ -32,7 +32,7 @@ export type UserFormProps = { max?: number, search?: string ) => Promise; - addGroups?: (newReps: GroupRepresentation[]) => void; + addGroup?: (newGroup: GroupRepresentation) => void; }; export const UserGroups = () => { @@ -48,17 +48,12 @@ export const UserGroups = () => { const [search, setSearch] = useState(""); const [username, setUsername] = useState(""); - const [join, setJoin] = useState(); - - const lastId = getLastId(location.pathname); - const [isDirectMembership, setDirectMembership] = useState(true); const [directMembershipList, setDirectMembershipList] = useState< GroupRepresentation[] >([]); const [open, setOpen] = useState(false); - const [move, setMove] = useState(); const adminClient = useAdminClient(); const { id } = useParams<{ id: string }>(); @@ -210,15 +205,6 @@ export const UserGroups = () => { const toggleModal = () => { setOpen(!open); }; - const JoinGroupButtonRenderer = (group: GroupRepresentation) => { - return ( - <> - - - ); - }; const joinGroup = (group: GroupRepresentation) => { setSelectedGroup(group); From 10b3619ae905f3d64b4f1d0c94cf27235dacf5ab Mon Sep 17 00:00:00 2001 From: jenny-s51 Date: Fri, 16 Apr 2021 16:15:02 -0400 Subject: [PATCH 5/9] wip add groups to user form --- src/user/JoinGroupDialog.tsx | 88 +++++++++++++-------- src/user/UserForm.tsx | 144 ++++++++++++++++++++++++++++++++++- src/user/messages.json | 1 + src/user/user-section.css | 14 ++++ 4 files changed, 216 insertions(+), 31 deletions(-) diff --git a/src/user/JoinGroupDialog.tsx b/src/user/JoinGroupDialog.tsx index 2802acd5bd..8f417028e2 100644 --- a/src/user/JoinGroupDialog.tsx +++ b/src/user/JoinGroupDialog.tsx @@ -31,8 +31,9 @@ export type JoinGroupDialogProps = { open: boolean; toggleDialog: () => void; onClose: () => void; - username: string; + username?: string; onConfirm: (newGroups: Group[]) => void; + chips?: any; }; type Group = GroupRepresentation & { @@ -45,6 +46,7 @@ export const JoinGroupDialog = ({ toggleDialog, onConfirm, username, + chips, }: JoinGroupDialogProps) => { const { t } = useTranslation("roles"); const adminClient = useAdminClient(); @@ -61,41 +63,67 @@ export const JoinGroupDialog = ({ const { id } = useParams<{ id: string }>(); - useEffect( - () => - asyncStateFetch( - async () => { - const existingUserGroups = await adminClient.users.listGroups({ id }); - const allGroups = await adminClient.groups.find(); + if (id) { + 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]); - } + 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]); + } - groups.forEach((group: Group) => { - group.checked = !!selectedRows.find((r) => r.id === group.id); - }); - setGroups(groups); - }, - errorHandler - ), - [groupId] - ); + groups.forEach((group: Group) => { + group.checked = !!selectedRows.find((r) => r.id === group.id); + }); + setGroups(groups); + }, + errorHandler + ), + [groupId] + ); + } + + else if (!id) { + useEffect(() => { + return asyncStateFetch( + () => { + // adminClient.groups.find(); + return Promise.resolve(adminClient.groups.find()); + }, + (groups) => { + console.log(groups); + console.log("potato", chips) + // setGroups(groups.filter((item) => item.name !== chips)); + setGroups([...groups.filter((row) => !chips.includes(row.name))]); + + // setupForm(realm); + }, + errorHandler + ); + }, []); +} return ( ; @@ -45,6 +51,10 @@ export const UserForm = ({ const watchUsernameInput = watch("username"); const [timestamp, setTimestamp] = useState(null); + const [chips, setChips] = useState<(string | undefined)[]>([]); + + const [list, setList] = useState(false); + const [open, setOpen] = useState(false); useEffect(() => { if (editMode) { @@ -56,7 +66,7 @@ export const UserForm = ({ handleError ); } - }, []); + }, [chips]); const setupForm = (user: UserRepresentation) => { reset(); @@ -90,6 +100,82 @@ export const UserForm = ({ setRequiredUserActionsDropdownOpen(false); }; + const deleteItem = (id: string) => { + const copyOfChips = chips; + console.log("care", id); + + setChips(copyOfChips.filter((item) => item !== id)); + + // const index = copyOfChips.indexOf(id); + // if (index !== -1) { + // copyOfChips.splice(index, 1); + // setChips(copyOfChips); + // } + }; + + const deleteCategory = () => { + setChips([]); + }; + + const addChips = async (groups: GroupRepresentation[]): Promise => { + const newGroups = groups; + + const newGroupNames: (string | undefined)[] = newGroups!.map( + (item) => item.name + ); + console.log(newGroupNames); + setChips([...chips!, ...newGroupNames]); + + console.log("newGroups", newGroups) + + newGroups.forEach(async (group) => { + // try { + await adminClient.users.addToGroup({ + id: id, + groupId: group.id!, + }); + // refresh(); + // addAlert(t("users:addedGroupMembership"), AlertVariant.success); + // } catch (error) { + // // addAlert( + // // t("users:addedGroupMembershipError", { error }), + // // AlertVariant.danger + // // ); + // } + }); + + console.log("beep beep", adminClient.users.listGroups()) + + }; + + console.log(watchUsernameInput) + + + const addGroups = async (groups: GroupRepresentation[]): Promise => { + const newGroups = groups; + + newGroups.forEach(async (group) => { + try { + await adminClient.users.addToGroup({ + id: id, + groupId: group.id!, + }); + setList(true); + // refresh(); + // addAlert(t("users:addedGroupMembership"), AlertVariant.success); + } catch (error) { + // addAlert( + // t("users:addedGroupMembershipError", { error }), + // AlertVariant.danger + // ); + } + }); + }; + + const toggleModal = () => { + setOpen(!open); + }; + return ( + {open && ( + setOpen(!open)} + onConfirm={editMode ? addGroups : addChips} + toggleDialog={() => toggleModal()} + chips={chips} + /> + )} {editMode ? ( <> + {!editMode && ( + + } + > + ( + <> + + + {chips.map((currentChip) => ( + deleteItem(currentChip!)} + > + {currentChip} + + ))} + + + + + )} + /> + + )} + diff --git a/src/user/UsersTabs.tsx b/src/user/UsersTabs.tsx index 658f469a92..39d1b0ecff 100644 --- a/src/user/UsersTabs.tsx +++ b/src/user/UsersTabs.tsx @@ -17,6 +17,7 @@ import { useHistory, useParams, useRouteMatch } from "react-router-dom"; import { KeycloakTabs } from "../components/keycloak-tabs/KeycloakTabs"; import { UserGroups } from "./UserGroups"; import { UserConsents } from "./UserConsents"; +import GroupRepresentation from "keycloak-admin/lib/defs/groupRepresentation"; export const UsersTabs = () => { const { t } = useTranslation("roles"); @@ -28,6 +29,7 @@ export const UsersTabs = () => { const userForm = useForm({ mode: "onChange" }); const { id } = useParams<{ id: string }>(); const [user, setUser] = useState(""); + const [addedGroups, setAddedGroups] = useState([]); useEffect(() => { const update = async () => { @@ -39,13 +41,25 @@ export const UsersTabs = () => { setTimeout(update, 100); }, []); + const updateGroups = (groups: GroupRepresentation[]) => { + setAddedGroups(groups); + }; + const save = async (user: UserRepresentation) => { try { if (id) { await adminClient.users.update({ id: user.id! }, user); addAlert(t("users:userSaved"), AlertVariant.success); } else { - await adminClient.users.create(user); + const getNewUserId = await adminClient.users.create(user); + + addedGroups.forEach(async (group) => { + await adminClient.users.addToGroup({ + id: getNewUserId.id!, + groupId: group.id!, + }); + }); + addAlert(t("users:userCreated"), AlertVariant.success); history.push(url.substr(0, url.lastIndexOf("/"))); } @@ -70,7 +84,12 @@ export const UsersTabs = () => { data-testid="user-details-tab" title={{t("details")}} > - + { )} - {!id && } + {!id && ( + + )} ); From cbce18f5f7e06150c3f227d2314a85a9d34fef0b Mon Sep 17 00:00:00 2001 From: jenny-s51 Date: Tue, 20 Apr 2021 11:08:33 -0400 Subject: [PATCH 7/9] navigate to subgroups in modal --- src/user/JoinGroupDialog.tsx | 70 ++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 39 deletions(-) diff --git a/src/user/JoinGroupDialog.tsx b/src/user/JoinGroupDialog.tsx index 318db6c744..8c473c1fce 100644 --- a/src/user/JoinGroupDialog.tsx +++ b/src/user/JoinGroupDialog.tsx @@ -63,52 +63,44 @@ export const JoinGroupDialog = ({ const { id } = useParams<{ id: string }>(); - if (id) { - useEffect( - () => - asyncStateFetch( - async () => { + useEffect( + () => + asyncStateFetch( + async () => { + const allGroups = await adminClient.groups.find(); + + if (groupId) { + const group = await adminClient.groups.findOne({ id: groupId }); + return { group, groups: group.subGroups! }; + } else if (id) { 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]); - } - - groups.forEach((group: Group) => { - group.checked = !!selectedRows.find((r) => r.id === group.id); - }); - setGroups(groups); - }, - errorHandler - ), - [groupId] - ); - } else if (!id) { - useEffect(() => { - return asyncStateFetch( - () => { - return Promise.resolve(adminClient.groups.find()); + return { + groups: _.differenceBy(allGroups, existingUserGroups, "id"), + }; + } else + return { + groups: allGroups, + }; }, - (groups) => { - setGroups([...groups.filter((row) => !chips.includes(row.name))]); + async ({ group: selectedGroup, groups }) => { + if (selectedGroup) { + setNavigation([...navigation, selectedGroup]); + } + + groups.forEach((group: Group) => { + group.checked = !!selectedRows.find((r) => r.id === group.id); + }); + id + ? setGroups(groups) + : setGroups([...groups.filter((row) => !chips.includes(row.name))]); }, errorHandler - ); - }, []); - } + ), + [groupId] + ); return ( Date: Wed, 21 Apr 2021 09:53:21 -0400 Subject: [PATCH 8/9] address PR feedback from Mark --- src/user/JoinGroupDialog.tsx | 2 +- src/user/messages.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/user/JoinGroupDialog.tsx b/src/user/JoinGroupDialog.tsx index 8c473c1fce..feaf843e63 100644 --- a/src/user/JoinGroupDialog.tsx +++ b/src/user/JoinGroupDialog.tsx @@ -106,7 +106,7 @@ export const JoinGroupDialog = ({ Date: Wed, 21 Apr 2021 09:55:14 -0400 Subject: [PATCH 9/9] remove comment in cypress test --- cypress/integration/users_test.spec.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/cypress/integration/users_test.spec.ts b/cypress/integration/users_test.spec.ts index faebcf4871..70c1531937 100644 --- a/cypress/integration/users_test.spec.ts +++ b/cypress/integration/users_test.spec.ts @@ -16,7 +16,6 @@ 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 () { @@ -37,7 +36,6 @@ describe("Group creation", () => { masthead.checkNotificationMessage("Group created"); sidebarPage.goToGroups(); - // listingPage.searchItem(groupName, false).itemExist(groupName); } it("Add groups to be joined", () => {