fixing issue (#709)

* fixing issue

fixing: #680

* added kebab on row

* added more clear group seperation
This commit is contained in:
Erik Jan de Wit 2021-06-21 19:55:51 +02:00 committed by GitHub
parent c3809a32e6
commit a964abed8d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 97 additions and 5 deletions

View file

@ -0,0 +1,48 @@
import React from "react";
import { Tooltip } from "@patternfly/react-core";
import type { TableTextProps } from "@patternfly/react-table";
import type GroupRepresentation from "keycloak-admin/lib/defs/groupRepresentation";
type GroupPathProps = TableTextProps & {
group: GroupRepresentation;
};
const MAX_LENGTH = 20;
const PART = 10;
const truncatePath = (path?: string) => {
if (path && path.length >= MAX_LENGTH) {
return (
path.substr(0, PART) +
"..." +
path.substr(path.length - PART, path.length)
);
}
return path;
};
export const GroupPath = ({
group: { path },
onMouseEnter: onMouseEnterProp = () => {},
...props
}: GroupPathProps) => {
const [tooltip, setTooltip] = React.useState("");
const onMouseEnter = (event: any) => {
setTooltip(path!);
onMouseEnterProp(event);
};
const text = (
<span onMouseEnter={onMouseEnter} {...props}>
{truncatePath(path)}
</span>
);
return tooltip !== "" ? (
<Tooltip content={tooltip} isVisible>
{text}
</Tooltip>
) : (
text
);
};

View file

@ -1,5 +1,5 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { useLocation } from "react-router-dom"; import { Link, useLocation } from "react-router-dom";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import _ from "lodash"; import _ from "lodash";
import { import {
@ -16,6 +16,7 @@ import type GroupRepresentation from "keycloak-admin/lib/defs/groupRepresentatio
import type UserRepresentation from "keycloak-admin/lib/defs/userRepresentation"; import type UserRepresentation from "keycloak-admin/lib/defs/userRepresentation";
import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable"; import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable";
import { useAdminClient } from "../context/auth/AdminClient"; import { useAdminClient } from "../context/auth/AdminClient";
import { useRealm } from "../context/realm-context/RealmContext";
import { useAlerts } from "../components/alert/Alerts"; import { useAlerts } from "../components/alert/Alerts";
import { emptyFormatter } from "../util"; import { emptyFormatter } from "../util";
@ -23,6 +24,7 @@ import { getLastId } from "./groupIdUtils";
import { useSubGroups } from "./SubGroupsContext"; import { useSubGroups } from "./SubGroupsContext";
import { MemberModal } from "./MembersModal"; import { MemberModal } from "./MembersModal";
import { ListEmptyState } from "../components/list-empty-state/ListEmptyState"; import { ListEmptyState } from "../components/list-empty-state/ListEmptyState";
import { GroupPath } from "../components/group/GroupPath";
type MembersOf = UserRepresentation & { type MembersOf = UserRepresentation & {
membership: GroupRepresentation[]; membership: GroupRepresentation[];
@ -31,6 +33,7 @@ type MembersOf = UserRepresentation & {
export const Members = () => { export const Members = () => {
const { t } = useTranslation("groups"); const { t } = useTranslation("groups");
const adminClient = useAdminClient(); const adminClient = useAdminClient();
const { realm } = useRealm();
const { addAlert } = useAlerts(); const { addAlert } = useAlerts();
const location = useLocation(); const location = useLocation();
const id = getLastId(location.pathname); const id = getLastId(location.pathname);
@ -84,13 +87,23 @@ export const Members = () => {
const MemberOfRenderer = (member: MembersOf) => { const MemberOfRenderer = (member: MembersOf) => {
return ( return (
<> <>
{member.membership.map((group) => ( {member.membership.map((group, index) => (
<>{group.path} </> <>
<GroupPath key={group.id} group={group} />
{member.membership[index + 1] ? ", " : ""}
</>
))} ))}
</> </>
); );
}; };
const UserDetailLink = (user: MembersOf) => (
<>
<Link key={user.id} to={`/${realm}/users/${user.id}/settings`}>
{user.username}
</Link>
</>
);
return ( return (
<> <>
{addMembers && ( {addMembers && (
@ -132,7 +145,10 @@ export const Members = () => {
<ToolbarItem> <ToolbarItem>
<Dropdown <Dropdown
toggle={ toggle={
<KebabToggle onToggle={() => setIsKebabOpen(!isKebabOpen)} /> <KebabToggle
onToggle={() => setIsKebabOpen(!isKebabOpen)}
isDisabled={selectedRows.length === 0}
/>
} }
isOpen={isKebabOpen} isOpen={isKebabOpen}
isPlain isPlain
@ -169,10 +185,29 @@ export const Members = () => {
</ToolbarItem> </ToolbarItem>
</> </>
} }
actions={[
{
title: t("leave"),
onRowClick: async (user) => {
try {
await adminClient.users.delFromGroup({
id: user.id!,
groupId: id!,
});
addAlert(t("usersLeft", { count: 1 }), AlertVariant.success);
} catch (error) {
addAlert(t("usersLeftError"), AlertVariant.danger);
}
return true;
},
},
]}
columns={[ columns={[
{ {
name: "username", name: "username",
displayKey: "common:name", displayKey: "common:name",
cellRenderer: UserDetailLink,
}, },
{ {
name: "email", name: "email",

View file

@ -21,6 +21,7 @@ import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable
import { useAdminClient } from "../context/auth/AdminClient"; import { useAdminClient } from "../context/auth/AdminClient";
import { useRealm } from "../context/realm-context/RealmContext"; import { useRealm } from "../context/realm-context/RealmContext";
import { ListEmptyState } from "../components/list-empty-state/ListEmptyState"; import { ListEmptyState } from "../components/list-empty-state/ListEmptyState";
import { GroupPath } from "../components/group/GroupPath";
import { useSubGroups } from "./SubGroupsContext"; import { useSubGroups } from "./SubGroupsContext";
type SearchGroup = GroupRepresentation & { type SearchGroup = GroupRepresentation & {
@ -101,6 +102,8 @@ export const SearchGroups = () => {
return result; return result;
}; };
const Path = (group: GroupRepresentation) => <GroupPath group={group} />;
return ( return (
<> <>
<PageSection variant={PageSectionVariants.light}> <PageSection variant={PageSectionVariants.light}>
@ -155,6 +158,7 @@ export const SearchGroups = () => {
{ {
name: "path", name: "path",
displayKey: "groups:path", displayKey: "groups:path",
cellRenderer: Path,
}, },
]} ]}
emptyState={ emptyState={

View file

@ -22,6 +22,7 @@ import type UserRepresentation from "keycloak-admin/lib/defs/userRepresentation"
import { GroupPickerDialog } from "../components/group/GroupPickerDialog"; import { GroupPickerDialog } from "../components/group/GroupPickerDialog";
import { HelpContext } from "../components/help-enabler/HelpHeader"; import { HelpContext } from "../components/help-enabler/HelpHeader";
import { QuestionCircleIcon } from "@patternfly/react-icons"; import { QuestionCircleIcon } from "@patternfly/react-icons";
import { GroupPath } from "../components/group/GroupPath";
type GroupTableData = GroupRepresentation & { type GroupTableData = GroupRepresentation & {
membersLength?: number; membersLength?: number;
@ -271,6 +272,8 @@ export const UserGroups = () => {
}); });
}; };
const Path = (group: GroupRepresentation) => <GroupPath group={group} />;
return ( return (
<> <>
<PageSection variant="light"> <PageSection variant="light">
@ -345,7 +348,7 @@ export const UserGroups = () => {
{ {
name: "path", name: "path",
displayKey: "users:path", displayKey: "users:path",
cellFormatters: [emptyFormatter()], cellRenderer: Path,
transforms: [cellWidth(45)], transforms: [cellWidth(45)],
}, },
@ -362,6 +365,8 @@ export const UserGroups = () => {
hasIcon={true} hasIcon={true}
message={t("noGroups")} message={t("noGroups")}
instructions={t("noGroupsText")} instructions={t("noGroupsText")}
primaryActionText={t("joinGroup")}
onPrimaryAction={toggleModal}
/> />
) : ( ) : (
"" ""