2020-12-14 08:57:05 +00:00
|
|
|
import React, { useState } from "react";
|
2020-09-15 19:54:52 +00:00
|
|
|
import { useTranslation } from "react-i18next";
|
2020-09-28 15:58:03 +00:00
|
|
|
import {
|
|
|
|
Button,
|
|
|
|
Dropdown,
|
|
|
|
DropdownItem,
|
|
|
|
KebabToggle,
|
|
|
|
PageSection,
|
|
|
|
PageSectionVariants,
|
|
|
|
ToolbarItem,
|
2020-10-27 23:45:35 +00:00
|
|
|
AlertVariant,
|
2020-09-28 15:58:03 +00:00
|
|
|
} from "@patternfly/react-core";
|
2020-12-14 08:57:05 +00:00
|
|
|
import { UsersIcon } from "@patternfly/react-icons";
|
2020-11-12 12:55:52 +00:00
|
|
|
import GroupRepresentation from "keycloak-admin/lib/defs/groupRepresentation";
|
2020-09-09 09:07:17 +00:00
|
|
|
|
2020-12-14 08:57:05 +00:00
|
|
|
import { GroupsCreateModal } from "./GroupsCreateModal";
|
|
|
|
import { ViewHeader } from "../components/view-header/ViewHeader";
|
|
|
|
import { ListEmptyState } from "../components/list-empty-state/ListEmptyState";
|
|
|
|
import { useAdminClient } from "../context/auth/AdminClient";
|
|
|
|
import { useAlerts } from "../components/alert/Alerts";
|
|
|
|
import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable";
|
|
|
|
|
|
|
|
import "./GroupsSection.css";
|
2021-01-05 19:49:33 +00:00
|
|
|
import { Link, useRouteMatch } from "react-router-dom";
|
2020-12-14 08:57:05 +00:00
|
|
|
|
|
|
|
type GroupTableData = GroupRepresentation & {
|
|
|
|
membersLength?: number;
|
|
|
|
};
|
|
|
|
|
2020-09-10 18:04:03 +00:00
|
|
|
export const GroupsSection = () => {
|
2020-09-15 19:54:52 +00:00
|
|
|
const { t } = useTranslation("groups");
|
2020-11-12 12:55:52 +00:00
|
|
|
const adminClient = useAdminClient();
|
2020-09-28 15:58:03 +00:00
|
|
|
const [isKebabOpen, setIsKebabOpen] = useState(false);
|
2020-09-30 13:55:18 +00:00
|
|
|
const [createGroupName, setCreateGroupName] = useState("");
|
2020-10-13 20:52:23 +00:00
|
|
|
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
|
2020-12-14 08:57:05 +00:00
|
|
|
const [selectedRows, setSelectedRows] = useState<GroupRepresentation[]>([]);
|
2020-10-27 23:45:35 +00:00
|
|
|
const { addAlert } = useAlerts();
|
2021-01-05 19:49:33 +00:00
|
|
|
const { url } = useRouteMatch();
|
2020-12-14 08:57:05 +00:00
|
|
|
const [key, setKey] = useState("");
|
|
|
|
const refresh = () => setKey(`${new Date().getTime()}`);
|
|
|
|
|
|
|
|
const getMembers = async (id: string) => {
|
|
|
|
const response = await adminClient.groups.listMembers({ id });
|
|
|
|
return response ? response.length : 0;
|
|
|
|
};
|
2020-09-15 19:54:52 +00:00
|
|
|
|
|
|
|
const loader = async () => {
|
2020-11-12 12:55:52 +00:00
|
|
|
const groupsData = await adminClient.groups.find();
|
|
|
|
|
2020-12-14 08:57:05 +00:00
|
|
|
const memberPromises = groupsData.map((group) => getMembers(group.id!));
|
2020-09-28 15:58:03 +00:00
|
|
|
const memberData = await Promise.all(memberPromises);
|
2020-12-14 08:57:05 +00:00
|
|
|
const updatedObject = groupsData.map((group: GroupTableData, i) => {
|
|
|
|
group.membersLength = memberData[i];
|
|
|
|
return group;
|
2020-09-28 15:58:03 +00:00
|
|
|
});
|
2020-12-14 08:57:05 +00:00
|
|
|
return updatedObject;
|
2020-09-15 19:54:52 +00:00
|
|
|
};
|
|
|
|
|
2020-10-01 18:52:18 +00:00
|
|
|
const handleModalToggle = () => {
|
2020-10-01 19:15:23 +00:00
|
|
|
setIsCreateModalOpen(!isCreateModalOpen);
|
2020-10-01 18:52:18 +00:00
|
|
|
};
|
|
|
|
|
2020-12-14 08:57:05 +00:00
|
|
|
const deleteGroup = (group: GroupRepresentation) => {
|
|
|
|
try {
|
|
|
|
return adminClient.groups.del({
|
|
|
|
id: group.id!,
|
2020-10-27 23:45:35 +00:00
|
|
|
});
|
2020-12-14 08:57:05 +00:00
|
|
|
} catch (error) {
|
|
|
|
addAlert(t("groupDeleteError", { error }), AlertVariant.danger);
|
|
|
|
}
|
|
|
|
};
|
2020-10-27 23:45:35 +00:00
|
|
|
|
2020-12-14 08:57:05 +00:00
|
|
|
const multiDelete = async () => {
|
|
|
|
if (selectedRows!.length !== 0) {
|
|
|
|
const chainedPromises = selectedRows!.map((group) => deleteGroup(group));
|
|
|
|
|
|
|
|
await Promise.all(chainedPromises);
|
|
|
|
addAlert(t("groupsDeleted"), AlertVariant.success);
|
|
|
|
setSelectedRows([]);
|
|
|
|
refresh();
|
2020-10-27 23:45:35 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-12-14 08:57:05 +00:00
|
|
|
const GroupNameCell = (group: GroupTableData) => (
|
|
|
|
<>
|
2021-01-05 19:49:33 +00:00
|
|
|
<Link key={group.id} to={`${url}/${group.id}`}>
|
2020-12-14 08:57:05 +00:00
|
|
|
{group.name}
|
|
|
|
</Link>
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
|
|
|
|
const GroupMemberCell = (group: GroupTableData) => (
|
|
|
|
<div className="keycloak-admin--groups__member-count">
|
|
|
|
<UsersIcon key={`user-icon-${group.id}`} />
|
|
|
|
{group.membersLength}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
|
2020-09-15 19:54:52 +00:00
|
|
|
return (
|
2020-10-14 20:47:29 +00:00
|
|
|
<>
|
2020-10-01 19:15:23 +00:00
|
|
|
<ViewHeader titleKey="groups:groups" subKey="groups:groupsDescription" />
|
2020-09-28 15:58:03 +00:00
|
|
|
<PageSection variant={PageSectionVariants.light}>
|
2020-12-14 08:57:05 +00:00
|
|
|
<KeycloakDataTable
|
|
|
|
key={key}
|
|
|
|
onSelect={(rows) => setSelectedRows([...rows])}
|
|
|
|
canSelectAll={false}
|
|
|
|
loader={loader}
|
|
|
|
ariaLabelKey="client-scopes:clientScopeList"
|
|
|
|
searchPlaceholderKey="client-scopes:searchFor"
|
|
|
|
toolbarItem={
|
|
|
|
<>
|
|
|
|
<ToolbarItem>
|
|
|
|
<Button variant="primary" onClick={handleModalToggle}>
|
|
|
|
{t("createGroup")}
|
|
|
|
</Button>
|
|
|
|
</ToolbarItem>
|
|
|
|
<ToolbarItem>
|
|
|
|
<Dropdown
|
|
|
|
toggle={
|
|
|
|
<KebabToggle
|
|
|
|
onToggle={() => setIsKebabOpen(!isKebabOpen)}
|
2020-10-13 21:06:58 +00:00
|
|
|
/>
|
2020-12-14 08:57:05 +00:00
|
|
|
}
|
|
|
|
isOpen={isKebabOpen}
|
|
|
|
isPlain
|
|
|
|
dropdownItems={[
|
|
|
|
<DropdownItem
|
|
|
|
key="action"
|
|
|
|
component="button"
|
|
|
|
onClick={() => {
|
|
|
|
multiDelete();
|
|
|
|
setIsKebabOpen(false);
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
{t("common:delete")}
|
|
|
|
</DropdownItem>,
|
|
|
|
]}
|
2020-10-14 20:53:32 +00:00
|
|
|
/>
|
2020-12-14 08:57:05 +00:00
|
|
|
</ToolbarItem>
|
|
|
|
</>
|
|
|
|
}
|
|
|
|
actions={[
|
|
|
|
{
|
|
|
|
title: t("moveTo"),
|
|
|
|
onRowClick: () => console.log("TO DO: Add move to functionality"),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
title: t("common:delete"),
|
|
|
|
onRowClick: async (group: GroupRepresentation) => {
|
|
|
|
await deleteGroup(group);
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
},
|
|
|
|
]}
|
|
|
|
columns={[
|
|
|
|
{
|
|
|
|
name: "name",
|
|
|
|
displayKey: "groups:groupName",
|
|
|
|
cellRenderer: GroupNameCell,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "members",
|
|
|
|
displayKey: "groups:members",
|
|
|
|
cellRenderer: GroupMemberCell,
|
|
|
|
},
|
|
|
|
]}
|
|
|
|
emptyState={
|
|
|
|
<ListEmptyState
|
|
|
|
hasIcon={true}
|
|
|
|
message={t("noGroupsInThisRealm")}
|
|
|
|
instructions={t("noGroupsInThisRealmInstructions")}
|
|
|
|
primaryActionText={t("createGroup")}
|
|
|
|
onPrimaryAction={() => handleModalToggle()}
|
|
|
|
/>
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
|
2020-10-27 23:45:35 +00:00
|
|
|
<GroupsCreateModal
|
|
|
|
isCreateModalOpen={isCreateModalOpen}
|
|
|
|
handleModalToggle={handleModalToggle}
|
|
|
|
setIsCreateModalOpen={setIsCreateModalOpen}
|
|
|
|
createGroupName={createGroupName}
|
|
|
|
setCreateGroupName={setCreateGroupName}
|
2020-12-14 08:57:05 +00:00
|
|
|
refresh={refresh}
|
2020-10-27 23:45:35 +00:00
|
|
|
/>
|
2020-09-18 08:04:55 +00:00
|
|
|
</PageSection>
|
2020-10-14 20:47:29 +00:00
|
|
|
</>
|
2020-09-15 19:54:52 +00:00
|
|
|
);
|
2020-09-09 09:07:17 +00:00
|
|
|
};
|