diff --git a/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties b/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties index f2618e63de9..848a105c30e 100644 --- a/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties +++ b/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties @@ -1055,6 +1055,8 @@ identityProviderEntityId=Identity provider entity ID userInfoSignedResponseAlgorithm=User info signed response algorithm selectGroup=Select group selectOrgGroup=Select Organization Group +groupType=Group type +groupTypeHelp=Indicates whether the selected group is a realm group or an organization group. This determines where the mapper will look for the group when it is executed. scopePermissions.groups.view-members-description=Policies that decide if an administrator can view the members of this group tableOfGroups=Table of groups allowed-protocol-mappers.tooltip=List of allowed protocol mapper providers. If there is an attempt to register client, which contains some protocol mappers, which were not allowed, registration request will be rejected. diff --git a/js/apps/admin-ui/src/components/dynamic/GroupComponent.tsx b/js/apps/admin-ui/src/components/dynamic/GroupComponent.tsx index 608e39f88c2..5627b9901fa 100644 --- a/js/apps/admin-ui/src/components/dynamic/GroupComponent.tsx +++ b/js/apps/admin-ui/src/components/dynamic/GroupComponent.tsx @@ -8,7 +8,7 @@ import { FormGroup, } from "@patternfly/react-core"; import { useState } from "react"; -import { Controller, useFormContext } from "react-hook-form"; +import { Controller, useFormContext, useWatch } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { HelpItem } from "@keycloak/keycloak-ui-shared"; @@ -17,6 +17,7 @@ import { useGroupResource, GroupResourceContext, } from "../../context/group-resource/GroupResourceContext"; +import { useServerInfo } from "../../context/server-info/ServerInfoProvider"; import { GroupPickerDialog } from "../group/GroupPickerDialog"; import type { ComponentProps } from "./components"; @@ -31,10 +32,27 @@ export const GroupComponent = ({ const [open, setOpen] = useState(false); const [openOrgGroups, setOpenOrgGroups] = useState(false); const [groups, setGroups] = useState(); - const { control } = useFormContext(); + const { control, setValue } = useFormContext(); const { adminClient } = useAdminClient(); + const serverInfo = useServerInfo(); const hasLinkedOrganization = useGroupResource().isOrgGroups(); + const groupTypeFieldName = convertToName("groupType"); + // Get group type enum values from server + const groupTypes = serverInfo.enums?.["type"] || []; + const GROUP_TYPE_REALM = + groupTypes.find((t: string) => t === "REALM") || "REALM"; + const GROUP_TYPE_ORG = + groupTypes.find((t: string) => t === "ORGANIZATION") || "ORGANIZATION"; + + const groupType = useWatch({ + name: groupTypeFieldName, + control, + defaultValue: GROUP_TYPE_REALM, + }); + + const shouldRenderOrgField = + hasLinkedOrganization || groupType == GROUP_TYPE_ORG; return ( { field.onChange(groups?.[0].path); + setValue(groupTypeFieldName, GROUP_TYPE_REALM); setGroups(groups); setOpen(false); }} @@ -69,6 +88,7 @@ export const GroupComponent = ({ }} onConfirm={(groups) => { field.onChange(groups?.[0].path); + setValue(groupTypeFieldName, GROUP_TYPE_ORG); setGroups(groups); setOpenOrgGroups(false); }} @@ -89,7 +109,20 @@ export const GroupComponent = ({ {field.value && ( - field.onChange(undefined)}> + { + field.onChange(undefined); + setValue(groupTypeFieldName, undefined); + }} + > + {shouldRenderOrgField && ( + <> + {groupType === GROUP_TYPE_REALM + ? t("realm") + : t("organization")} + :  + + )} {field.value} )} @@ -105,7 +138,7 @@ export const GroupComponent = ({ {t("selectGroup")} - {hasLinkedOrganization && ( + {shouldRenderOrgField && (