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:
parent
c503663e4e
commit
a0faba0f97
7 changed files with 48 additions and 37 deletions
|
@ -159,17 +159,21 @@ export const AttributesForm = ({
|
||||||
)}
|
)}
|
||||||
</Tr>
|
</Tr>
|
||||||
))}
|
))}
|
||||||
<Button
|
<Tr>
|
||||||
aria-label={t("roles:addAttributeText")}
|
<Td>
|
||||||
id="plus-icon"
|
<Button
|
||||||
variant="link"
|
aria-label={t("roles:addAttributeText")}
|
||||||
className="kc-attributes__plus-icon"
|
id="plus-icon"
|
||||||
onClick={() => append({ key: "", value: "" })}
|
variant="link"
|
||||||
icon={<PlusCircleIcon />}
|
className="kc-attributes__plus-icon"
|
||||||
isDisabled={!watchLast}
|
onClick={() => append({ key: "", value: "" })}
|
||||||
>
|
icon={<PlusCircleIcon />}
|
||||||
{t("roles:addAttributeText")}
|
isDisabled={!watchLast}
|
||||||
</Button>
|
>
|
||||||
|
{t("roles:addAttributeText")}
|
||||||
|
</Button>
|
||||||
|
</Td>
|
||||||
|
</Tr>
|
||||||
</Tbody>
|
</Tbody>
|
||||||
</TableComposable>
|
</TableComposable>
|
||||||
<ActionGroup className="kc-attributes__action-group">
|
<ActionGroup className="kc-attributes__action-group">
|
||||||
|
|
|
@ -16,7 +16,6 @@ import { Spinner } from "@patternfly/react-core";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
|
||||||
import { PaginatingTableToolbar } from "./PaginatingTableToolbar";
|
import { PaginatingTableToolbar } from "./PaginatingTableToolbar";
|
||||||
import { TableToolbar } from "./TableToolbar";
|
|
||||||
import { asyncStateFetch } from "../../context/auth/AdminClient";
|
import { asyncStateFetch } from "../../context/auth/AdminClient";
|
||||||
import { ListEmptyState } from "../list-empty-state/ListEmptyState";
|
import { ListEmptyState } from "../list-empty-state/ListEmptyState";
|
||||||
|
|
||||||
|
@ -56,11 +55,7 @@ function DataTable<T>({
|
||||||
}
|
}
|
||||||
canSelectAll={canSelectAll}
|
canSelectAll={canSelectAll}
|
||||||
cells={columns.map((column) => {
|
cells={columns.map((column) => {
|
||||||
return {
|
return { ...column, title: t(column.displayKey || column.name) };
|
||||||
...column,
|
|
||||||
title: t(column.displayKey || column.name),
|
|
||||||
transforms: column.transforms,
|
|
||||||
};
|
|
||||||
})}
|
})}
|
||||||
rows={rows}
|
rows={rows}
|
||||||
actions={actions}
|
actions={actions}
|
||||||
|
|
|
@ -20,9 +20,11 @@ export const AliasRendererComponent = ({
|
||||||
const [containerName, setContainerName] = useState<string>("");
|
const [containerName, setContainerName] = useState<string>("");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
adminClient.clients
|
if (filterType === "clients") {
|
||||||
.findOne({ id: containerId! })
|
adminClient.clients
|
||||||
.then((client) => setContainerName(client.clientId! as string));
|
.findOne({ id: containerId! })
|
||||||
|
.then((client) => setContainerName(client.clientId! as string));
|
||||||
|
}
|
||||||
}, [containerId]);
|
}, [containerId]);
|
||||||
|
|
||||||
if (filterType === "roles" || !containerName) {
|
if (filterType === "roles" || !containerName) {
|
||||||
|
|
|
@ -44,14 +44,20 @@ export const AssociatedRolesModal = (props: AssociatedRolesModalProps) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const loader = async () => {
|
const loader = async () => {
|
||||||
const allRoles = await adminClient.roles.find();
|
const roles = await adminClient.roles.find();
|
||||||
const existingAdditionalRoles = await adminClient.roles.getCompositeRoles({
|
const existingAdditionalRoles = await adminClient.roles.getCompositeRoles({
|
||||||
id,
|
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 (
|
return (
|
||||||
existingAdditionalRoles.find(
|
props.existingCompositeRoles.find(
|
||||||
(existing: RoleRepresentation) => existing.name === role.name
|
(existing: RoleRepresentation) => existing.name === role.name
|
||||||
) === undefined && role.name !== name
|
) === undefined && role.name !== name
|
||||||
);
|
);
|
||||||
|
|
|
@ -22,7 +22,6 @@ import { RoleFormType } from "./RealmRoleTabs";
|
||||||
import ClientRepresentation from "keycloak-admin/lib/defs/clientRepresentation";
|
import ClientRepresentation from "keycloak-admin/lib/defs/clientRepresentation";
|
||||||
import { AliasRendererComponent } from "./AliasRendererComponent";
|
import { AliasRendererComponent } from "./AliasRendererComponent";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { cellWidth } from "@patternfly/react-table";
|
|
||||||
|
|
||||||
type AssociatedRolesTabProps = {
|
type AssociatedRolesTabProps = {
|
||||||
additionalRoles: RoleRepresentation[];
|
additionalRoles: RoleRepresentation[];
|
||||||
|
@ -47,6 +46,7 @@ export const AssociatedRolesTab = ({
|
||||||
|
|
||||||
const [selectedRows, setSelectedRows] = useState<RoleRepresentation[]>([]);
|
const [selectedRows, setSelectedRows] = useState<RoleRepresentation[]>([]);
|
||||||
const [isInheritedHidden, setIsInheritedHidden] = useState(false);
|
const [isInheritedHidden, setIsInheritedHidden] = useState(false);
|
||||||
|
const [allRoles, setAllRoles] = useState<RoleRepresentation[]>([]);
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
@ -85,12 +85,12 @@ export const AssociatedRolesTab = ({
|
||||||
return newRoles;
|
return newRoles;
|
||||||
};
|
};
|
||||||
|
|
||||||
const alphabetize = (rolesList: RoleRepresentation[]) => {
|
|
||||||
return _.sortBy(rolesList, (role) => role.name?.toUpperCase());
|
|
||||||
};
|
|
||||||
|
|
||||||
const loader = async () => {
|
const loader = async () => {
|
||||||
|
const alphabetize = (rolesList: RoleRepresentation[]) => {
|
||||||
|
return _.sortBy(rolesList, (role) => role.name?.toUpperCase());
|
||||||
|
};
|
||||||
if (isInheritedHidden) {
|
if (isInheritedHidden) {
|
||||||
|
setAllRoles(additionalRoles);
|
||||||
return alphabetize(additionalRoles);
|
return alphabetize(additionalRoles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +106,13 @@ export const AssociatedRolesTab = ({
|
||||||
);
|
);
|
||||||
|
|
||||||
return fetchedRoles.then((results: RoleRepresentation[]) => {
|
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({
|
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
|
||||||
titleKey: "roles:roleRemoveAssociatedRoleConfirm",
|
titleKey: "roles:roleRemoveAssociatedRoleConfirm",
|
||||||
messageKey: t("roles:roleRemoveAssociatedText"),
|
messageKey: t("roles:roleRemoveAssociatedText"),
|
||||||
continueButtonLabel: t("common:remove"),
|
continueButtonLabel: "common:delete",
|
||||||
continueButtonVariant: ButtonVariant.danger,
|
continueButtonVariant: ButtonVariant.danger,
|
||||||
onConfirm: async () => {
|
onConfirm: async () => {
|
||||||
try {
|
try {
|
||||||
await adminClient.roles.delCompositeRoles({ id }, selectedRows);
|
await adminClient.roles.delCompositeRoles({ id }, selectedRows);
|
||||||
|
onRemove(selectedRows);
|
||||||
setSelectedRows([]);
|
setSelectedRows([]);
|
||||||
|
|
||||||
addAlert(t("associatedRolesRemoved"), AlertVariant.success);
|
addAlert(t("associatedRolesRemoved"), AlertVariant.success);
|
||||||
|
@ -158,11 +165,11 @@ export const AssociatedRolesTab = ({
|
||||||
messageKey: t("roles:removeAllAssociatedRolesConfirmDialog", {
|
messageKey: t("roles:removeAllAssociatedRolesConfirmDialog", {
|
||||||
name: parentRole?.name || t("createRole"),
|
name: parentRole?.name || t("createRole"),
|
||||||
}),
|
}),
|
||||||
continueButtonLabel: "common:remove",
|
continueButtonLabel: "common:delete",
|
||||||
continueButtonVariant: ButtonVariant.danger,
|
continueButtonVariant: ButtonVariant.danger,
|
||||||
onConfirm: async () => {
|
onConfirm: async () => {
|
||||||
try {
|
try {
|
||||||
if (selectedRows.length === additionalRoles.length) {
|
if (selectedRows.length === allRoles.length) {
|
||||||
onRemove(selectedRows);
|
onRemove(selectedRows);
|
||||||
const loc = url.replace(/\/AssociatedRoles/g, "/details");
|
const loc = url.replace(/\/AssociatedRoles/g, "/details");
|
||||||
history.push(loc);
|
history.push(loc);
|
||||||
|
@ -170,6 +177,7 @@ export const AssociatedRolesTab = ({
|
||||||
onRemove(selectedRows);
|
onRemove(selectedRows);
|
||||||
await adminClient.roles.delCompositeRoles({ id }, selectedRows);
|
await adminClient.roles.delCompositeRoles({ id }, selectedRows);
|
||||||
addAlert(t("associatedRolesRemoved"), AlertVariant.success);
|
addAlert(t("associatedRolesRemoved"), AlertVariant.success);
|
||||||
|
refresh();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
addAlert(`${t("roleDeleteError")} ${error}`, AlertVariant.danger);
|
addAlert(`${t("roleDeleteError")} ${error}`, AlertVariant.danger);
|
||||||
}
|
}
|
||||||
|
@ -246,20 +254,17 @@ export const AssociatedRolesTab = ({
|
||||||
displayKey: "roles:roleName",
|
displayKey: "roles:roleName",
|
||||||
cellRenderer: AliasRenderer,
|
cellRenderer: AliasRenderer,
|
||||||
cellFormatters: [formattedLinkTableCell(), emptyFormatter()],
|
cellFormatters: [formattedLinkTableCell(), emptyFormatter()],
|
||||||
transforms: [cellWidth(40)],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "containerId",
|
name: "containerId",
|
||||||
displayKey: "roles:inheritedFrom",
|
displayKey: "roles:inheritedFrom",
|
||||||
cellRenderer: InheritedRoleName,
|
cellRenderer: InheritedRoleName,
|
||||||
cellFormatters: [emptyFormatter()],
|
cellFormatters: [emptyFormatter()],
|
||||||
transforms: [cellWidth(30)],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "description",
|
name: "description",
|
||||||
displayKey: "common:description",
|
displayKey: "common:description",
|
||||||
cellFormatters: [emptyFormatter()],
|
cellFormatters: [emptyFormatter()],
|
||||||
transforms: [cellWidth(30)],
|
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
emptyState={
|
emptyState={
|
||||||
|
|
|
@ -57,6 +57,7 @@ export const RealmRoleTabs = () => {
|
||||||
const [additionalRoles, setAdditionalRoles] = useState<RoleRepresentation[]>(
|
const [additionalRoles, setAdditionalRoles] = useState<RoleRepresentation[]>(
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
const { addAlert } = useAlerts();
|
const { addAlert } = useAlerts();
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
@ -321,7 +322,6 @@ export const RealmRoleTabs = () => {
|
||||||
addComposites={addComposites}
|
addComposites={addComposites}
|
||||||
parentRole={role!}
|
parentRole={role!}
|
||||||
onRemove={() => refresh()}
|
onRemove={() => refresh()}
|
||||||
// client={client!}
|
|
||||||
/>
|
/>
|
||||||
</Tab>
|
</Tab>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
|
@ -61,7 +61,6 @@ export const UserForm = ({
|
||||||
const setupForm = (user: UserRepresentation) => {
|
const setupForm = (user: UserRepresentation) => {
|
||||||
reset();
|
reset();
|
||||||
Object.entries(user).map((entry) => {
|
Object.entries(user).map((entry) => {
|
||||||
console.log(entry[0], entry[1]);
|
|
||||||
if (entry[0] == "createdTimestamp") {
|
if (entry[0] == "createdTimestamp") {
|
||||||
setTimestamp(entry[1]);
|
setTimestamp(entry[1]);
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in a new issue