Fix roles pagination+filter out duplicates from associated roles list (#460)

* realm roles UX review progress wip

* filter realm roles on Enter key press, add filter functionality

* clean up

* filterChips logic now in table toolbar

* fix lint and format

* save with erik

* remove filter chips functionality

* fix check-types

* fix realm roles cypress test

* format

* wip pagination

* rebase

* fix roles pagination

* format

* add back save

* remove duplicates in associated roles table, can now paginate modal

* remove logs

* PR feedback from Erik

* rebase and fix pagination/search

* remove slice

* pagination in modal and associated roles tab

* show client roles

* lint and format

* remove unused variable

* address tbody console warning, clean up log stmts

* clean up log stmts

* fix ts error in AliasRenderer

* fix lint

* lint

* PR feedback from Erik

* resolve conflicts and format

* comment
This commit is contained in:
Eugenia 2021-04-01 09:41:21 -04:00 committed by GitHub
parent c503663e4e
commit a0faba0f97
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 48 additions and 37 deletions

View file

@ -159,17 +159,21 @@ export const AttributesForm = ({
)}
</Tr>
))}
<Button
aria-label={t("roles:addAttributeText")}
id="plus-icon"
variant="link"
className="kc-attributes__plus-icon"
onClick={() => append({ key: "", value: "" })}
icon={<PlusCircleIcon />}
isDisabled={!watchLast}
>
{t("roles:addAttributeText")}
</Button>
<Tr>
<Td>
<Button
aria-label={t("roles:addAttributeText")}
id="plus-icon"
variant="link"
className="kc-attributes__plus-icon"
onClick={() => append({ key: "", value: "" })}
icon={<PlusCircleIcon />}
isDisabled={!watchLast}
>
{t("roles:addAttributeText")}
</Button>
</Td>
</Tr>
</Tbody>
</TableComposable>
<ActionGroup className="kc-attributes__action-group">

View file

@ -16,7 +16,6 @@ import { Spinner } from "@patternfly/react-core";
import _ from "lodash";
import { PaginatingTableToolbar } from "./PaginatingTableToolbar";
import { TableToolbar } from "./TableToolbar";
import { asyncStateFetch } from "../../context/auth/AdminClient";
import { ListEmptyState } from "../list-empty-state/ListEmptyState";
@ -56,11 +55,7 @@ function DataTable<T>({
}
canSelectAll={canSelectAll}
cells={columns.map((column) => {
return {
...column,
title: t(column.displayKey || column.name),
transforms: column.transforms,
};
return { ...column, title: t(column.displayKey || column.name) };
})}
rows={rows}
actions={actions}

View file

@ -20,9 +20,11 @@ export const AliasRendererComponent = ({
const [containerName, setContainerName] = useState<string>("");
useEffect(() => {
adminClient.clients
.findOne({ id: containerId! })
.then((client) => setContainerName(client.clientId! as string));
if (filterType === "clients") {
adminClient.clients
.findOne({ id: containerId! })
.then((client) => setContainerName(client.clientId! as string));
}
}, [containerId]);
if (filterType === "roles" || !containerName) {

View file

@ -44,14 +44,20 @@ export const AssociatedRolesModal = (props: AssociatedRolesModalProps) => {
};
const loader = async () => {
const allRoles = await adminClient.roles.find();
const roles = await adminClient.roles.find();
const existingAdditionalRoles = await adminClient.roles.getCompositeRoles({
id,
});
const allRoles = [...roles, ...existingAdditionalRoles];
return alphabetize(allRoles).filter((role: RoleRepresentation) => {
const filterDupes = allRoles.filter(
(thing, index, self) =>
index === self.findIndex((t) => t.name === thing.name)
);
return alphabetize(filterDupes).filter((role: RoleRepresentation) => {
return (
existingAdditionalRoles.find(
props.existingCompositeRoles.find(
(existing: RoleRepresentation) => existing.name === role.name
) === undefined && role.name !== name
);

View file

@ -22,7 +22,6 @@ import { RoleFormType } from "./RealmRoleTabs";
import ClientRepresentation from "keycloak-admin/lib/defs/clientRepresentation";
import { AliasRendererComponent } from "./AliasRendererComponent";
import _ from "lodash";
import { cellWidth } from "@patternfly/react-table";
type AssociatedRolesTabProps = {
additionalRoles: RoleRepresentation[];
@ -47,6 +46,7 @@ export const AssociatedRolesTab = ({
const [selectedRows, setSelectedRows] = useState<RoleRepresentation[]>([]);
const [isInheritedHidden, setIsInheritedHidden] = useState(false);
const [allRoles, setAllRoles] = useState<RoleRepresentation[]>([]);
const [open, setOpen] = useState(false);
@ -85,12 +85,12 @@ export const AssociatedRolesTab = ({
return newRoles;
};
const alphabetize = (rolesList: RoleRepresentation[]) => {
return _.sortBy(rolesList, (role) => role.name?.toUpperCase());
};
const loader = async () => {
const alphabetize = (rolesList: RoleRepresentation[]) => {
return _.sortBy(rolesList, (role) => role.name?.toUpperCase());
};
if (isInheritedHidden) {
setAllRoles(additionalRoles);
return alphabetize(additionalRoles);
}
@ -106,7 +106,13 @@ export const AssociatedRolesTab = ({
);
return fetchedRoles.then((results: RoleRepresentation[]) => {
return alphabetize(results);
const filterDupes = results.filter(
(thing, index, self) =>
index === self.findIndex((t) => t.name === thing.name)
);
setAllRoles(filterDupes);
return alphabetize(filterDupes);
});
};
@ -136,11 +142,12 @@ export const AssociatedRolesTab = ({
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
titleKey: "roles:roleRemoveAssociatedRoleConfirm",
messageKey: t("roles:roleRemoveAssociatedText"),
continueButtonLabel: t("common:remove"),
continueButtonLabel: "common:delete",
continueButtonVariant: ButtonVariant.danger,
onConfirm: async () => {
try {
await adminClient.roles.delCompositeRoles({ id }, selectedRows);
onRemove(selectedRows);
setSelectedRows([]);
addAlert(t("associatedRolesRemoved"), AlertVariant.success);
@ -158,11 +165,11 @@ export const AssociatedRolesTab = ({
messageKey: t("roles:removeAllAssociatedRolesConfirmDialog", {
name: parentRole?.name || t("createRole"),
}),
continueButtonLabel: "common:remove",
continueButtonLabel: "common:delete",
continueButtonVariant: ButtonVariant.danger,
onConfirm: async () => {
try {
if (selectedRows.length === additionalRoles.length) {
if (selectedRows.length === allRoles.length) {
onRemove(selectedRows);
const loc = url.replace(/\/AssociatedRoles/g, "/details");
history.push(loc);
@ -170,6 +177,7 @@ export const AssociatedRolesTab = ({
onRemove(selectedRows);
await adminClient.roles.delCompositeRoles({ id }, selectedRows);
addAlert(t("associatedRolesRemoved"), AlertVariant.success);
refresh();
} catch (error) {
addAlert(`${t("roleDeleteError")} ${error}`, AlertVariant.danger);
}
@ -246,20 +254,17 @@ export const AssociatedRolesTab = ({
displayKey: "roles:roleName",
cellRenderer: AliasRenderer,
cellFormatters: [formattedLinkTableCell(), emptyFormatter()],
transforms: [cellWidth(40)],
},
{
name: "containerId",
displayKey: "roles:inheritedFrom",
cellRenderer: InheritedRoleName,
cellFormatters: [emptyFormatter()],
transforms: [cellWidth(30)],
},
{
name: "description",
displayKey: "common:description",
cellFormatters: [emptyFormatter()],
transforms: [cellWidth(30)],
},
]}
emptyState={

View file

@ -57,6 +57,7 @@ export const RealmRoleTabs = () => {
const [additionalRoles, setAdditionalRoles] = useState<RoleRepresentation[]>(
[]
);
const { addAlert } = useAlerts();
const [open, setOpen] = useState(false);
@ -321,7 +322,6 @@ export const RealmRoleTabs = () => {
addComposites={addComposites}
parentRole={role!}
onRemove={() => refresh()}
// client={client!}
/>
</Tab>
) : null}

View file

@ -61,7 +61,6 @@ export const UserForm = ({
const setupForm = (user: UserRepresentation) => {
reset();
Object.entries(user).map((entry) => {
console.log(entry[0], entry[1]);
if (entry[0] == "createdTimestamp") {
setTimestamp(entry[1]);
} else {