From 5ea204b003b77e359e91199532fffee9bdee3a91 Mon Sep 17 00:00:00 2001 From: jenny-s51 Date: Tue, 8 Jun 2021 09:23:32 -0400 Subject: [PATCH 1/6] wip consents UI fixes --- src/user/UserConsents.tsx | 114 ++++++++++++++++++++++---------------- src/user/UsersTabs.tsx | 4 +- src/user/user-section.css | 4 ++ 3 files changed, 72 insertions(+), 50 deletions(-) diff --git a/src/user/UserConsents.tsx b/src/user/UserConsents.tsx index c2eb778562..96026ef79b 100644 --- a/src/user/UserConsents.tsx +++ b/src/user/UserConsents.tsx @@ -1,7 +1,7 @@ -import React from "react"; +import React, { useEffect, useState } from "react"; import { useParams } from "react-router-dom"; import { useTranslation } from "react-i18next"; -import { PageSection } from "@patternfly/react-core"; +import { Button, Label, PageSection } from "@patternfly/react-core"; import { ListEmptyState } from "../components/list-empty-state/ListEmptyState"; import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable"; import { emptyFormatter } from "../util"; @@ -27,10 +27,31 @@ export const UserConsents = () => { return alphabetize(consents); }; + const [labelClicked, setLabelClicked] = useState(false); + + useEffect(() => { + console.log(labelClicked) + }, [labelClicked]) + const clientScopesRenderer = ({ grantedClientScopes, }: UserConsentRepresentation) => { - return <>{grantedClientScopes!.join(", ")}; + if (grantedClientScopes!.length <= 5 && labelClicked) { + return <>{grantedClientScopes!.join(", ")}; + } else + return ( + <> + {grantedClientScopes!.slice(1, 6).join(", ")}{" "} + + + ); }; const createdRenderer = ({ createDate }: UserConsentRepresentation) => { @@ -45,50 +66,49 @@ export const UserConsents = () => { return ( <> - - {}} - /> - } - /> - + {}} + /> + } + /> ); }; diff --git a/src/user/UsersTabs.tsx b/src/user/UsersTabs.tsx index 698897b22b..169e634149 100644 --- a/src/user/UsersTabs.tsx +++ b/src/user/UsersTabs.tsx @@ -108,9 +108,7 @@ export const UsersTabs = () => { data-testid="user-consents-tab" title={{t("users:consents")}} > - - - + )} diff --git a/src/user/user-section.css b/src/user/user-section.css index c28f7cbcfd..1c3a2173d4 100644 --- a/src/user/user-section.css +++ b/src/user/user-section.css @@ -45,3 +45,7 @@ td.pf-c-table__check > input { width: 20px; vertical-align: text-top; } + +.pf-c-toolbar__content-section { + margin-bottom: calc(var(--pf-global--spacer--lg) * -1); +} From 67b6c52c65d1cea693c8ba648a20ae97a01e5db4 Mon Sep 17 00:00:00 2001 From: jenny-s51 Date: Tue, 8 Jun 2021 13:56:03 -0400 Subject: [PATCH 2/6] consents wip --- src/user/UserConsents.tsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/user/UserConsents.tsx b/src/user/UserConsents.tsx index 96026ef79b..5151f644b6 100644 --- a/src/user/UserConsents.tsx +++ b/src/user/UserConsents.tsx @@ -1,7 +1,7 @@ -import React, { useEffect, useState } from "react"; +import React, { useState } from "react"; import { useParams } from "react-router-dom"; import { useTranslation } from "react-i18next"; -import { Button, Label, PageSection } from "@patternfly/react-core"; +import { Label } from "@patternfly/react-core"; import { ListEmptyState } from "../components/list-empty-state/ListEmptyState"; import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable"; import { emptyFormatter } from "../util"; @@ -29,9 +29,11 @@ export const UserConsents = () => { const [labelClicked, setLabelClicked] = useState(false); - useEffect(() => { - console.log(labelClicked) - }, [labelClicked]) + // useEffect(() => { + // console.log(labelClicked) + // }, [key]) + + const [key, setKey] = useState(0); const clientScopesRenderer = ({ grantedClientScopes, @@ -68,6 +70,7 @@ export const UserConsents = () => { <> Date: Wed, 9 Jun 2021 09:35:35 -0400 Subject: [PATCH 3/6] consents fixes done --- src/user/UserConsents.tsx | 45 ++++++++++++++----------------------- src/user/user-section.css | 47 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 28 deletions(-) diff --git a/src/user/UserConsents.tsx b/src/user/UserConsents.tsx index 5151f644b6..fab50c408b 100644 --- a/src/user/UserConsents.tsx +++ b/src/user/UserConsents.tsx @@ -1,7 +1,7 @@ -import React, { useState } from "react"; +import React from "react"; import { useParams } from "react-router-dom"; import { useTranslation } from "react-i18next"; -import { Label } from "@patternfly/react-core"; +import { Chip, ChipGroup } from "@patternfly/react-core"; import { ListEmptyState } from "../components/list-empty-state/ListEmptyState"; import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable"; import { emptyFormatter } from "../util"; @@ -22,38 +22,28 @@ export const UserConsents = () => { }; const loader = async () => { - const consents = await adminClient.users.listConsents({ id }); + const getConsents = await adminClient.users.listConsents({ id }); - return alphabetize(consents); + return alphabetize(getConsents); }; - const [labelClicked, setLabelClicked] = useState(false); - - // useEffect(() => { - // console.log(labelClicked) - // }, [key]) - - const [key, setKey] = useState(0); - const clientScopesRenderer = ({ grantedClientScopes, }: UserConsentRepresentation) => { - if (grantedClientScopes!.length <= 5 && labelClicked) { - return <>{grantedClientScopes!.join(", ")}; - } else - return ( - <> - {grantedClientScopes!.slice(1, 6).join(", ")}{" "} - - - ); + {currentChip} + + ))} + + ); }; const createdRenderer = ({ createDate }: UserConsentRepresentation) => { @@ -70,7 +60,6 @@ export const UserConsents = () => { <> input { .pf-c-toolbar__content-section { margin-bottom: calc(var(--pf-global--spacer--lg) * -1); } + +.pf-c-chip.kc-consents-chip::before { + padding: 0px; + border: 0; +} + +.pf-c-chip.kc-consents-chip { + padding: 0px; +} + +.pf-c-chip-group__list-item { + margin-right: 0px; +} + +#consents-chip-text.pf-c-chip-text { + font-size: var(--pf-c-table--cell--FontSize); +} + +.kc-consents-chip > .pf-c-chip__text { + font-size: var(--pf-c-table--cell--FontSize); + display: inline-block; +} + +.pf-c-chip-group.kc-consents-chip-group + > div.pf-c-chip-group__main + > ul.pf-c-chip-group__list + li:first-child + .pf-c-chip__text::before { + content: ""; +} + +.pf-c-chip-group.kc-consents-chip-group + > div.pf-c-chip-group__main + > ul.pf-c-chip-group__list + .pf-c-chip__text::before { + content: ", "; +} + +.pf-c-chip-group.kc-consents-chip-group + > div.pf-c-chip-group__main + > ul.pf-c-chip-group__list + .pf-m-overflow + .pf-c-chip__text::before { + content: ""; + margin-left: var(--pf-global--spacer--sm); + padding-left: 0px; +} From aefac52144b111002db7248ec289e2bf33c53e93 Mon Sep 17 00:00:00 2001 From: jenny-s51 Date: Wed, 9 Jun 2021 14:01:14 -0400 Subject: [PATCH 4/6] add revoke action --- src/user/UserConsents.tsx | 53 ++++++++++++++++++++++++++++++++++++--- src/user/messages.json | 7 +++++- src/user/user-section.css | 17 ++++++++++--- 3 files changed, 69 insertions(+), 8 deletions(-) diff --git a/src/user/UserConsents.tsx b/src/user/UserConsents.tsx index fab50c408b..ac653ac4ff 100644 --- a/src/user/UserConsents.tsx +++ b/src/user/UserConsents.tsx @@ -1,7 +1,12 @@ -import React from "react"; +import React, { useState } from "react"; import { useParams } from "react-router-dom"; import { useTranslation } from "react-i18next"; -import { Chip, ChipGroup } from "@patternfly/react-core"; +import { + AlertVariant, + ButtonVariant, + Chip, + ChipGroup, +} from "@patternfly/react-core"; import { ListEmptyState } from "../components/list-empty-state/ListEmptyState"; import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable"; import { emptyFormatter } from "../util"; @@ -11,9 +16,16 @@ import _ from "lodash"; import type UserConsentRepresentation from "keycloak-admin/lib/defs/userConsentRepresentation"; import { CubesIcon } from "@patternfly/react-icons"; import moment from "moment"; +import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; +import { useAlerts } from "../components/alert/Alerts"; export const UserConsents = () => { + const [selectedClient, setSelectedClient] = useState< + UserConsentRepresentation + >(); const { t } = useTranslation("roles"); + const { addAlert } = useAlerts(); + const [key, setKey] = useState(0); const adminClient = useAdminClient(); const { id } = useParams<{ id: string }>(); @@ -21,6 +33,8 @@ export const UserConsents = () => { return _.sortBy(consentsList, (client) => client.clientId?.toUpperCase()); }; + const refresh = () => setKey(new Date().getTime()); + const loader = async () => { const getConsents = await adminClient.users.listConsents({ id }); @@ -56,10 +70,34 @@ export const UserConsents = () => { return <>{moment(lastUpdatedDate).format("MM/DD/YY hh:MM A")}; }; + const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({ + titleKey: "users:revokeClientScopesTitle", + messageKey: t("users:revokeClientScopes") + selectedClient?.clientId + "?", + continueButtonLabel: "common:delete", + continueButtonVariant: ButtonVariant.danger, + onConfirm: async () => { + try { + await adminClient.users.revokeConsent({ + id, + // realm: realm, + clientId: selectedClient!.clientId!, + }); + + refresh(); + + addAlert(t("deleteGrantsSuccess"), AlertVariant.success); + } catch (error) { + addAlert(t("deleteGrantsError", { error }), AlertVariant.danger); + } + }, + }); + return ( <> + { displayKey: "clients:lastUpdated", cellFormatters: [emptyFormatter()], cellRenderer: lastUpdatedRenderer, - transforms: [cellWidth(20)], + transforms: [cellWidth(10)], + }, + ]} + actions={[ + { + title: t("users:revoke"), + onRowClick: (client) => { + setSelectedClient(client); + toggleDeleteDialog(); + }, }, ]} emptyState={ diff --git a/src/user/messages.json b/src/user/messages.json index 17d55ec009..2b794844ed 100644 --- a/src/user/messages.json +++ b/src/user/messages.json @@ -60,6 +60,11 @@ "noConsents": "No consents", "noConsentsText": "The consents will only be recorded when users try to access a client that is configured to require consent. In that case, users will get a consent page which asks them to grant access to the client.", "whoWillAppearLinkText": "Who will appear in this group list?", - "whoWillAppearPopoverText": "Groups are hierarchical. When you select Direct Membership, you see only the child group that the user joined. Ancestor groups are not included." + "whoWillAppearPopoverText": "Groups are hierarchical. When you select Direct Membership, you see only the child group that the user joined. Ancestor groups are not included.", + "revoke": "Revoke", + "revokeClientScopesTitle": "Revoke all granted client scopes?", + "revokeClientScopes": "Are you sure you want to revoke all granted client scopes for ", + "deleteGrantsSuccess": "Grants successfully revoked.", + "deleteGrantsError": "Error deleting grants." } } diff --git a/src/user/user-section.css b/src/user/user-section.css index 8273cb9ddc..f601d2980d 100644 --- a/src/user/user-section.css +++ b/src/user/user-section.css @@ -87,12 +87,21 @@ td.pf-c-table__check > input { content: ", "; } -.pf-c-chip-group.kc-consents-chip-group +div.pf-c-chip-group.kc-consents-chip-group > div.pf-c-chip-group__main > ul.pf-c-chip-group__list - .pf-m-overflow - .pf-c-chip__text::before { + > li.pf-c-chip-group__list-item:last-child + > button.pf-c-chip.pf-m-overflow::before { + content: ""; + margin-left: var(--pf-global--spacer--sm); +} + +div.pf-c-chip-group.kc-consents-chip-group + > div.pf-c-chip-group__main + > ul.pf-c-chip-group__list + > li.pf-c-chip-group__list-item:last-child + > button.pf-c-chip.pf-m-overflow + > span::before { content: ""; margin-left: var(--pf-global--spacer--sm); - padding-left: 0px; } From 76f47f17f2f26d388710feb2b534a655c1e0d51e Mon Sep 17 00:00:00 2001 From: jenny-s51 Date: Wed, 9 Jun 2021 14:01:41 -0400 Subject: [PATCH 5/6] remove comment --- src/user/UserConsents.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/user/UserConsents.tsx b/src/user/UserConsents.tsx index ac653ac4ff..7be2f60236 100644 --- a/src/user/UserConsents.tsx +++ b/src/user/UserConsents.tsx @@ -79,7 +79,6 @@ export const UserConsents = () => { try { await adminClient.users.revokeConsent({ id, - // realm: realm, clientId: selectedClient!.clientId!, }); From f8d325b3f09658c8563f4e1e64389f51f90ffbdd Mon Sep 17 00:00:00 2001 From: jenny-s51 Date: Thu, 10 Jun 2021 11:08:11 -0400 Subject: [PATCH 6/6] fix localization --- src/user/UserConsents.tsx | 4 +++- src/user/messages.json | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/user/UserConsents.tsx b/src/user/UserConsents.tsx index 7be2f60236..97381a7e60 100644 --- a/src/user/UserConsents.tsx +++ b/src/user/UserConsents.tsx @@ -72,7 +72,9 @@ export const UserConsents = () => { const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({ titleKey: "users:revokeClientScopesTitle", - messageKey: t("users:revokeClientScopes") + selectedClient?.clientId + "?", + messageKey: t("users:revokeClientScopes", { + clientId: selectedClient?.clientId, + }), continueButtonLabel: "common:delete", continueButtonVariant: ButtonVariant.danger, onConfirm: async () => { diff --git a/src/user/messages.json b/src/user/messages.json index 2b794844ed..70a3264019 100644 --- a/src/user/messages.json +++ b/src/user/messages.json @@ -63,7 +63,7 @@ "whoWillAppearPopoverText": "Groups are hierarchical. When you select Direct Membership, you see only the child group that the user joined. Ancestor groups are not included.", "revoke": "Revoke", "revokeClientScopesTitle": "Revoke all granted client scopes?", - "revokeClientScopes": "Are you sure you want to revoke all granted client scopes for ", + "revokeClientScopes": "Are you sure you want to revoke all granted client scopes for {{clientId}}?", "deleteGrantsSuccess": "Grants successfully revoked.", "deleteGrantsError": "Error deleting grants." }