added filter to idp table and manage order (#32889)
* added filter to idp table and manage order fixes: #32780 Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com> * Update js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties Co-authored-by: Stefan Guilhen <sguilhen@redhat.com> Signed-off-by: Erik Jan de Wit <edewit@redhat.com> --------- Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com> Signed-off-by: Erik Jan de Wit <edewit@redhat.com> Co-authored-by: Stefan Guilhen <sguilhen@redhat.com>
This commit is contained in:
parent
3fe5d4847a
commit
808883c34d
4 changed files with 158 additions and 81 deletions
|
@ -1481,6 +1481,8 @@ mapperTypeMsadLdsUserAccountControlMapper=msad-user-account-control-mapper
|
||||||
realmSettingsExplain=Realm settings are settings that control the options for users, applications, roles, and groups in the current realm.
|
realmSettingsExplain=Realm settings are settings that control the options for users, applications, roles, and groups in the current realm.
|
||||||
mappingUpdatedError=Could not update mapping\: '{{error}}'
|
mappingUpdatedError=Could not update mapping\: '{{error}}'
|
||||||
manageDisplayOrder=Manage display order
|
manageDisplayOrder=Manage display order
|
||||||
|
emptyRealmBasedIdps=No realm based identity providers are configured for this realm.
|
||||||
|
hideOrganizationLinkedIdps=Hide organization linked identity providers
|
||||||
exactSearch=Exact search
|
exactSearch=Exact search
|
||||||
value=Value
|
value=Value
|
||||||
filenamePlaceholder=Upload a PEM file or paste key below
|
filenamePlaceholder=Upload a PEM file or paste key below
|
||||||
|
|
|
@ -1,12 +1,20 @@
|
||||||
import type IdentityProviderRepresentation from "@keycloak/keycloak-admin-client/lib/defs/identityProviderRepresentation";
|
import type IdentityProviderRepresentation from "@keycloak/keycloak-admin-client/lib/defs/identityProviderRepresentation";
|
||||||
import type { IdentityProvidersQuery } from "@keycloak/keycloak-admin-client/lib/resources/identityProviders";
|
import type { IdentityProvidersQuery } from "@keycloak/keycloak-admin-client/lib/resources/identityProviders";
|
||||||
import { IconMapper, useAlerts, useFetch } from "@keycloak/keycloak-ui-shared";
|
import {
|
||||||
|
Action,
|
||||||
|
IconMapper,
|
||||||
|
KeycloakDataTable,
|
||||||
|
ListEmptyState,
|
||||||
|
useAlerts,
|
||||||
|
useFetch,
|
||||||
|
} from "@keycloak/keycloak-ui-shared";
|
||||||
import {
|
import {
|
||||||
AlertVariant,
|
AlertVariant,
|
||||||
Badge,
|
Badge,
|
||||||
Button,
|
Button,
|
||||||
ButtonVariant,
|
ButtonVariant,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
|
Checkbox,
|
||||||
Dropdown,
|
Dropdown,
|
||||||
DropdownGroup,
|
DropdownGroup,
|
||||||
DropdownItem,
|
DropdownItem,
|
||||||
|
@ -28,7 +36,6 @@ import { Link, useNavigate } from "react-router-dom";
|
||||||
import { useAdminClient } from "../admin-client";
|
import { useAdminClient } from "../admin-client";
|
||||||
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
||||||
import { ClickableCard } from "../components/keycloak-card/ClickableCard";
|
import { ClickableCard } from "../components/keycloak-card/ClickableCard";
|
||||||
import { Action, KeycloakDataTable } from "@keycloak/keycloak-ui-shared";
|
|
||||||
import { ViewHeader } from "../components/view-header/ViewHeader";
|
import { ViewHeader } from "../components/view-header/ViewHeader";
|
||||||
import { useRealm } from "../context/realm-context/RealmContext";
|
import { useRealm } from "../context/realm-context/RealmContext";
|
||||||
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
|
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
|
||||||
|
@ -102,6 +109,7 @@ export default function IdentityProvidersSection() {
|
||||||
const [key, setKey] = useState(0);
|
const [key, setKey] = useState(0);
|
||||||
const refresh = () => setKey(key + 1);
|
const refresh = () => setKey(key + 1);
|
||||||
|
|
||||||
|
const [hide, setHide] = useState(false);
|
||||||
const [addProviderOpen, setAddProviderOpen] = useState(false);
|
const [addProviderOpen, setAddProviderOpen] = useState(false);
|
||||||
const [manageDisplayDialog, setManageDisplayDialog] = useState(false);
|
const [manageDisplayDialog, setManageDisplayDialog] = useState(false);
|
||||||
const [hasProviders, setHasProviders] = useState(false);
|
const [hasProviders, setHasProviders] = useState(false);
|
||||||
|
@ -121,6 +129,7 @@ export default function IdentityProvidersSection() {
|
||||||
const params: IdentityProvidersQuery = {
|
const params: IdentityProvidersQuery = {
|
||||||
first: first!,
|
first: first!,
|
||||||
max: max!,
|
max: max!,
|
||||||
|
realmOnly: hide,
|
||||||
};
|
};
|
||||||
if (search) {
|
if (search) {
|
||||||
params.search = search;
|
params.search = search;
|
||||||
|
@ -184,6 +193,7 @@ export default function IdentityProvidersSection() {
|
||||||
<DeleteConfirm />
|
<DeleteConfirm />
|
||||||
{manageDisplayDialog && (
|
{manageDisplayDialog && (
|
||||||
<ManageOrderDialog
|
<ManageOrderDialog
|
||||||
|
hideRealmBasedIdps={hide}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
setManageDisplayDialog(false);
|
setManageDisplayDialog(false);
|
||||||
refresh();
|
refresh();
|
||||||
|
@ -243,6 +253,18 @@ export default function IdentityProvidersSection() {
|
||||||
searchPlaceholderKey="searchForProvider"
|
searchPlaceholderKey="searchForProvider"
|
||||||
toolbarItem={
|
toolbarItem={
|
||||||
<>
|
<>
|
||||||
|
<ToolbarItem alignSelf="center">
|
||||||
|
<Checkbox
|
||||||
|
label={t("hideOrganizationLinkedIdps")}
|
||||||
|
id="hideOrganizationLinkedIdps"
|
||||||
|
data-testid="hideOrganizationLinkedIdps"
|
||||||
|
isChecked={hide}
|
||||||
|
onChange={(_event, check) => {
|
||||||
|
setHide(check);
|
||||||
|
refresh();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ToolbarItem>
|
||||||
<ToolbarItem>
|
<ToolbarItem>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
data-testid="addProviderDropdown"
|
data-testid="addProviderDropdown"
|
||||||
|
@ -299,6 +321,23 @@ export default function IdentityProvidersSection() {
|
||||||
cellRenderer: OrganizationLink,
|
cellRenderer: OrganizationLink,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
emptyState={
|
||||||
|
<ListEmptyState
|
||||||
|
message={t("identityProviders")}
|
||||||
|
instructions={t("emptyRealmBasedIdps")}
|
||||||
|
isSearchVariant
|
||||||
|
secondaryActions={[
|
||||||
|
{
|
||||||
|
text: t("clearAllFilters"),
|
||||||
|
onClick: () => {
|
||||||
|
setHide(false);
|
||||||
|
refresh();
|
||||||
|
},
|
||||||
|
type: ButtonVariant.link,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</PageSection>
|
</PageSection>
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
import type IdentityProviderRepresentation from "@keycloak/keycloak-admin-client/lib/defs/identityProviderRepresentation";
|
import type IdentityProviderRepresentation from "@keycloak/keycloak-admin-client/lib/defs/identityProviderRepresentation";
|
||||||
import { useAlerts, useFetch } from "@keycloak/keycloak-ui-shared";
|
import {
|
||||||
|
KeycloakSpinner,
|
||||||
|
useAlerts,
|
||||||
|
useFetch,
|
||||||
|
} from "@keycloak/keycloak-ui-shared";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
ButtonVariant,
|
ButtonVariant,
|
||||||
|
@ -23,13 +27,18 @@ import { sortBy } from "lodash-es";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useAdminClient } from "../admin-client";
|
import { useAdminClient } from "../admin-client";
|
||||||
import { KeycloakSpinner } from "@keycloak/keycloak-ui-shared";
|
|
||||||
|
|
||||||
type ManageOrderDialogProps = {
|
type ManageOrderDialogProps = {
|
||||||
|
orgId?: string;
|
||||||
|
hideRealmBasedIdps?: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ManageOrderDialog = ({ onClose }: ManageOrderDialogProps) => {
|
export const ManageOrderDialog = ({
|
||||||
|
orgId,
|
||||||
|
hideRealmBasedIdps = false,
|
||||||
|
onClose,
|
||||||
|
}: ManageOrderDialogProps) => {
|
||||||
const { adminClient } = useAdminClient();
|
const { adminClient } = useAdminClient();
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
@ -67,7 +76,10 @@ export const ManageOrderDialog = ({ onClose }: ManageOrderDialogProps) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
useFetch(
|
useFetch(
|
||||||
() => adminClient.identityProviders.find(),
|
() =>
|
||||||
|
orgId
|
||||||
|
? adminClient.organizations.listIdentityProviders({ orgId })
|
||||||
|
: adminClient.identityProviders.find({ realmOnly: hideRealmBasedIdps }),
|
||||||
(providers) => {
|
(providers) => {
|
||||||
setProviders(providers);
|
setProviders(providers);
|
||||||
setOrder(
|
setOrder(
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import IdentityProviderRepresentation from "@keycloak/keycloak-admin-client/lib/defs/identityProviderRepresentation";
|
import IdentityProviderRepresentation from "@keycloak/keycloak-admin-client/lib/defs/identityProviderRepresentation";
|
||||||
import {
|
import {
|
||||||
KeycloakDataTable,
|
KeycloakDataTable,
|
||||||
|
ListEmptyState,
|
||||||
useAlerts,
|
useAlerts,
|
||||||
useFetch,
|
useFetch,
|
||||||
} from "@keycloak/keycloak-ui-shared";
|
} from "@keycloak/keycloak-ui-shared";
|
||||||
|
@ -17,7 +18,7 @@ import { useTranslation } from "react-i18next";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { useAdminClient } from "../admin-client";
|
import { useAdminClient } from "../admin-client";
|
||||||
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
||||||
import { ListEmptyState } from "@keycloak/keycloak-ui-shared";
|
import { ManageOrderDialog } from "../identity-providers/ManageOrderDialog";
|
||||||
import useToggle from "../utils/useToggle";
|
import useToggle from "../utils/useToggle";
|
||||||
import { LinkIdentityProviderModal } from "./LinkIdentityProviderModal";
|
import { LinkIdentityProviderModal } from "./LinkIdentityProviderModal";
|
||||||
import { EditOrganizationParams } from "./routes/EditOrganization";
|
import { EditOrganizationParams } from "./routes/EditOrganization";
|
||||||
|
@ -74,6 +75,7 @@ export const IdentityProviders = () => {
|
||||||
const [key, setKey] = useState(0);
|
const [key, setKey] = useState(0);
|
||||||
const refresh = () => setKey(key + 1);
|
const refresh = () => setKey(key + 1);
|
||||||
|
|
||||||
|
const [manageDisplayDialog, setManageDisplayDialog] = useState(false);
|
||||||
const [hasProviders, setHasProviders] = useState(false);
|
const [hasProviders, setHasProviders] = useState(false);
|
||||||
const [selectedRow, setSelectedRow] =
|
const [selectedRow, setSelectedRow] =
|
||||||
useState<IdentityProviderRepresentation>();
|
useState<IdentityProviderRepresentation>();
|
||||||
|
@ -111,88 +113,110 @@ export const IdentityProviders = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageSection variant="light">
|
<>
|
||||||
<UnlinkConfirm />
|
{manageDisplayDialog && (
|
||||||
{open && (
|
<ManageOrderDialog
|
||||||
<LinkIdentityProviderModal
|
|
||||||
orgId={orgId!}
|
orgId={orgId!}
|
||||||
identityProvider={selectedRow}
|
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
toggleOpen();
|
setManageDisplayDialog(false);
|
||||||
refresh();
|
refresh();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{!hasProviders ? (
|
<PageSection variant="light">
|
||||||
<ListEmptyState
|
<UnlinkConfirm />
|
||||||
icon={BellIcon}
|
{open && (
|
||||||
message={t("noIdentityProvider")}
|
<LinkIdentityProviderModal
|
||||||
instructions={t("noIdentityProviderInstructions")}
|
orgId={orgId!}
|
||||||
/>
|
identityProvider={selectedRow}
|
||||||
) : (
|
onClose={() => {
|
||||||
<KeycloakDataTable
|
toggleOpen();
|
||||||
key={key}
|
refresh();
|
||||||
loader={loader}
|
}}
|
||||||
ariaLabelKey="identityProviders"
|
/>
|
||||||
searchPlaceholderKey="searchProvider"
|
)}
|
||||||
toolbarItem={
|
{!hasProviders ? (
|
||||||
<ToolbarItem>
|
<ListEmptyState
|
||||||
<Button
|
icon={BellIcon}
|
||||||
onClick={() => {
|
message={t("noIdentityProvider")}
|
||||||
setSelectedRow(undefined);
|
instructions={t("noIdentityProviderInstructions")}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<KeycloakDataTable
|
||||||
|
key={key}
|
||||||
|
loader={loader}
|
||||||
|
ariaLabelKey="identityProviders"
|
||||||
|
searchPlaceholderKey="searchProvider"
|
||||||
|
toolbarItem={
|
||||||
|
<>
|
||||||
|
<ToolbarItem>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedRow(undefined);
|
||||||
|
toggleOpen();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("linkIdentityProvider")}
|
||||||
|
</Button>
|
||||||
|
</ToolbarItem>
|
||||||
|
<ToolbarItem>
|
||||||
|
<Button
|
||||||
|
data-testid="manageDisplayOrder"
|
||||||
|
variant="link"
|
||||||
|
onClick={() => setManageDisplayDialog(true)}
|
||||||
|
>
|
||||||
|
{t("manageDisplayOrder")}
|
||||||
|
</Button>
|
||||||
|
</ToolbarItem>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
actions={[
|
||||||
|
{
|
||||||
|
title: t("edit"),
|
||||||
|
onRowClick: (row) => {
|
||||||
|
setSelectedRow(row);
|
||||||
toggleOpen();
|
toggleOpen();
|
||||||
}}
|
},
|
||||||
>
|
|
||||||
{t("linkIdentityProvider")}
|
|
||||||
</Button>
|
|
||||||
</ToolbarItem>
|
|
||||||
}
|
|
||||||
actions={[
|
|
||||||
{
|
|
||||||
title: t("edit"),
|
|
||||||
onRowClick: (row) => {
|
|
||||||
setSelectedRow(row);
|
|
||||||
toggleOpen();
|
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
{
|
title: t("unLinkIdentityProvider"),
|
||||||
title: t("unLinkIdentityProvider"),
|
onRowClick: (row) => {
|
||||||
onRowClick: (row) => {
|
setSelectedRow(row);
|
||||||
setSelectedRow(row);
|
toggleUnlinkDialog();
|
||||||
toggleUnlinkDialog();
|
},
|
||||||
},
|
},
|
||||||
},
|
]}
|
||||||
]}
|
columns={[
|
||||||
columns={[
|
{
|
||||||
{
|
name: "alias",
|
||||||
name: "alias",
|
},
|
||||||
},
|
{
|
||||||
{
|
name: "config['kc.org.domain']",
|
||||||
name: "config['kc.org.domain']",
|
displayKey: "domain",
|
||||||
displayKey: "domain",
|
},
|
||||||
},
|
{
|
||||||
{
|
name: "providerId",
|
||||||
name: "providerId",
|
displayKey: "providerDetails",
|
||||||
displayKey: "providerDetails",
|
},
|
||||||
},
|
{
|
||||||
{
|
name: "config['kc.org.broker.public']",
|
||||||
name: "config['kc.org.broker.public']",
|
displayKey: "shownOnLoginPage",
|
||||||
displayKey: "shownOnLoginPage",
|
cellRenderer: (row) => (
|
||||||
cellRenderer: (row) => (
|
<ShownOnLoginPageCheck row={row} refresh={refresh} />
|
||||||
<ShownOnLoginPageCheck row={row} refresh={refresh} />
|
),
|
||||||
),
|
},
|
||||||
},
|
]}
|
||||||
]}
|
emptyState={
|
||||||
emptyState={
|
<ListEmptyState
|
||||||
<ListEmptyState
|
message={t("emptyIdentityProviderLink")}
|
||||||
message={t("emptyIdentityProviderLink")}
|
instructions={t("emptyIdentityProviderLinkInstructions")}
|
||||||
instructions={t("emptyIdentityProviderLinkInstructions")}
|
primaryActionText={t("linkIdentityProvider")}
|
||||||
primaryActionText={t("linkIdentityProvider")}
|
onPrimaryAction={toggleOpen}
|
||||||
onPrimaryAction={toggleOpen}
|
/>
|
||||||
/>
|
}
|
||||||
}
|
/>
|
||||||
/>
|
)}
|
||||||
)}
|
</PageSection>
|
||||||
</PageSection>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue