fixing issue (#709)
* fixing issue fixing: #680 * added kebab on row * added more clear group seperation
This commit is contained in:
parent
c3809a32e6
commit
a964abed8d
4 changed files with 97 additions and 5 deletions
48
src/components/group/GroupPath.tsx
Normal file
48
src/components/group/GroupPath.tsx
Normal 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
|
||||||
|
);
|
||||||
|
};
|
|
@ -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",
|
||||||
|
|
|
@ -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={
|
||||||
|
|
|
@ -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}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
""
|
""
|
||||||
|
|
Loading…
Reference in a new issue