From 1f45fb89aa28b53d09ecf62c204ad5319165c79f Mon Sep 17 00:00:00 2001 From: agagancarczyk Date: Mon, 10 Jan 2022 10:31:50 +0000 Subject: [PATCH] Grouped multiple credentials (#1700) * expandable rows - wip * expandable rows - wip * expandable rows - wip * expandable rows - wip * expandable rows - wip * expandable rows - wip * expandable rows - wip * expandable rows - css improvements * expandable rows - css improvements * expandable rows - css improvements * expandable rows - css improvements * expandable rows - css improvements * expandable rows - css improvements * expandable rows - css improvements * expandable rows - css improvements * expandable rows - css improvements * expandable rows - css improvements * removed unnecessary css * css cleanup * feedback fixes * table refactor * table refactor * table refactor * table refactor * table refactor * small css fix Co-authored-by: Agnieszka Gancarczyk --- src/user/UserCredentials.tsx | 478 ++++++++++++++++++++++++---------- src/user/user-credentials.css | 69 +++++ src/user/user-section.css | 57 +--- 3 files changed, 406 insertions(+), 198 deletions(-) create mode 100644 src/user/user-credentials.css diff --git a/src/user/UserCredentials.tsx b/src/user/UserCredentials.tsx index 0d6e5b8b28..732804ba37 100644 --- a/src/user/UserCredentials.tsx +++ b/src/user/UserCredentials.tsx @@ -1,4 +1,4 @@ -import React, { FunctionComponent, useMemo, useState } from "react"; +import React, { Fragment, FunctionComponent, useMemo, useState } from "react"; import { AlertVariant, Button, @@ -27,7 +27,6 @@ import { TableComposable, TableHeader, TableVariant, - Tbody, Td, Th, Thead, @@ -44,7 +43,7 @@ import { useWhoAmI } from "../context/whoami/WhoAmI"; import { Controller, useForm, UseFormMethods, useWatch } from "react-hook-form"; import { PasswordInput } from "../components/password-input/PasswordInput"; import { HelpItem } from "../components/help-enabler/HelpItem"; -import "./user-section.css"; +import "./user-credentials.css"; import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; import type CredentialRepresentation from "@keycloak/keycloak-admin-client/lib/defs/credentialRepresentation"; import { FormAccess } from "../components/form-access/FormAccess"; @@ -90,6 +89,12 @@ const userLabelDefaultValues: UserLabelForm = { userLabel: "", }; +type ExpandableCredentialRepresentation = { + key: string; + value: CredentialRepresentation[]; + isExpanded: boolean; +}; + const DisplayDialog: FunctionComponent = ({ titleKey, onClose, @@ -241,6 +246,9 @@ export const UserCredentials = ({ user }: UserCredentialsProps) => { const [userCredentials, setUserCredentials] = useState< CredentialRepresentation[] >([]); + const [groupedUserCredentials, setGroupedUserCredentials] = useState< + ExpandableCredentialRepresentation[] + >([]); const [selectedCredential, setSelectedCredential] = useState({}); const [isResetPassword, setIsResetPassword] = useState(false); @@ -256,6 +264,23 @@ export const UserCredentials = ({ user }: UserCredentialsProps) => { () => adminClient.users.getCredentials({ id: user.id! }), (credentials) => { setUserCredentials(credentials); + + const groupedCredentials = credentials.reduce((r, a) => { + r[a.type!] = r[a.type!] || []; + r[a.type!].push(a); + return r; + }, Object.create(null)); + + const groupedCredentialsArray = Object.keys(groupedCredentials).map( + (key) => ({ key, value: groupedCredentials[key] }) + ); + + setGroupedUserCredentials( + groupedCredentialsArray.map((groupedCredential) => ({ + ...groupedCredential, + isExpanded: false, + })) + ); }, [key] ); @@ -671,7 +696,7 @@ export const UserCredentials = ({ user }: UserCredentialsProps) => { <> + + {credential.type === "password" ? ( + + + + ) : ( + + )} + + + setKebabOpen({ + status, + rowKey: credential.id!, + }) + } + /> + } + isOpen={ + kebabOpen.status && + kebabOpen.rowKey === credential.id + } + onSelect={() => { + setSelectedCredential(credential); + }} + dropdownItems={[ + { + toggleDeleteDialog(); + setKebabOpen({ + status: false, + rowKey: credential.id!, + }); + }} + > + {t("deleteBtn")} + , + ]} + /> + + + ))} + + {groupedCredential.isExpanded && + groupedCredential.value.map((credential) => ( + + + + {credential.type!.charAt(0).toUpperCase()! + + credential.type!.slice(1)} + + + -
- {isUserLabelEdit?.status && - isUserLabelEdit.rowKey === credential.id ? ( - <> - -
+ +
+ {isUserLabelEdit?.status && + isUserLabelEdit.rowKey === credential.id ? ( + <> + +
+
+ + ) : ( + <> + {credential.userLabel ?? ""}
- - ) : ( - <> - {credential.userLabel ?? ""} -
- - - - - - - {credential.type === "password" ? ( + + )} +
+ +
+ - ) : ( - )} - - - setKebabOpen({ - status, - rowKey: credential.id!, - }) - } - /> - } - isOpen={ - kebabOpen.status && kebabOpen.rowKey === credential.id - } - onSelect={() => { - setSelectedCredential(credential); - }} - dropdownItems={[ - { - toggleDeleteDialog(); - setKebabOpen({ - status: false, - rowKey: credential.id!, - }); - }} - > - {t("deleteBtn")} - , - ]} - /> - - - - ))} - + + + setKebabOpen({ + status, + rowKey: credential.id!, + }) + } + /> + } + isOpen={ + kebabOpen.status && + kebabOpen.rowKey === credential.id + } + onSelect={() => { + setSelectedCredential(credential); + }} + dropdownItems={[ + { + toggleDeleteDialog(); + setKebabOpen({ + status: false, + rowKey: credential.id!, + }); + }} + > + {t("deleteBtn")} + , + ]} + /> + + + ))} + + ))} ) : ( diff --git a/src/user/user-credentials.css b/src/user/user-credentials.css new file mode 100644 index 0000000000..5b943d4640 --- /dev/null +++ b/src/user/user-credentials.css @@ -0,0 +1,69 @@ + + +.kc-edit-icon { + color: var(--pf-global--Color--200); + margin-left: 5px; +} + +.kc-showData-btn { + padding-left: 0; +} + +.kc-userLabel-row { + display: inline-block !important; + width: 100%; +} + +.kc-form-group-userLabel, .kc-userLabel-actionBtns { + display: flex; +} + +.kc-editUserLabel-btn, .kc-editUserLabel-cancelBtn { + color: var(--pf-global--Color--200) !important; +} + +.kc-editUserLabel-btn { + padding-top: 0px; +} + +.kc-editUserLabel-btn:hover { + filter: brightness(55%); +} + +.kc-editUserLabel-acceptBtn { + padding-right: 8px; +} + +.kc-editUserLabel-cancelBtn { + padding-left: 8px !important; +} + +.pf-c-table.pf-m-compact tr:not(.pf-c-table__expandable-row)>:last-child { + overflow-wrap: anywhere; +} + +.kc-setPasswordBtn-tbl { + margin: 25px 0 25px 25px; +} + +.kc-form-userLabel { + max-height: 0px; + margin-bottom: 0px; + padding-bottom: 15px;; +} + +.kc-notExpandableRow-credentialType { + padding: 15px 0px 15px 15px !important; +} + +.kc-expandableRow-credentialType { + padding-left: 15px !important; +} + +.kc-expandRow-btn { + vertical-align: middle; +} + +.kc-temporaryPassword { + margin: 6px 0 10px 35px; +} diff --git a/src/user/user-section.css b/src/user/user-section.css index 96bd979757..3880a3b71d 100644 --- a/src/user/user-section.css +++ b/src/user/user-section.css @@ -119,61 +119,6 @@ article.pf-c-card.pf-m-flat.kc-available-idps > div > div > h1 { text-align: center; } -.kc-temporaryPassword { - margin: 6px 0 10px 35px; -} - .keycloak__user-credentials__reset-form { --pf-c-form--m-horizontal__group-label--md--GridColumnWidth: 13rem; -} - -.kc-edit-icon { - color: var(--pf-global--Color--200); - margin-left: 5px; -} - -.kc-showData-btn { - padding-left: 0; -} - -.kc-userLabel-row { - display: inline-block !important; - width: 50%; -} - -.kc-form-group-userLabel, .kc-userLabel-actionBtns { - display: flex; -} - -.kc-editUserLabel-btn, .kc-editUserLabel-cancelBtn { - color: var(--pf-global--Color--200) !important; -} - -.kc-editUserLabel-btn { - padding-top: 0px; -} - -.kc-editUserLabel-btn:hover { - filter: brightness(55%); -} - -.kc-editUserLabel-acceptBtn { - padding-right: 8px; -} - -.kc-editUserLabel-cancelBtn { - padding-left: 8px !important; -} - -.pf-c-table.pf-m-compact tr:not(.pf-c-table__expandable-row)>:last-child { - overflow-wrap: anywhere; -} - -.setPasswordBtn-table { - margin: 25px 0 25px 25px; -} - -.resetCredentialBtn-header { - margin: 10px 25px 10px 0; - float: right; -} +} \ No newline at end of file