2023-05-03 13:51:02 +00:00
|
|
|
import type GroupRepresentation from "@keycloak/keycloak-admin-client/lib/defs/groupRepresentation";
|
2022-02-09 11:46:58 +00:00
|
|
|
import {
|
|
|
|
AlertVariant,
|
|
|
|
Button,
|
|
|
|
ButtonVariant,
|
|
|
|
Dropdown,
|
|
|
|
DropdownItem,
|
|
|
|
KebabToggle,
|
|
|
|
Popover,
|
|
|
|
Text,
|
|
|
|
TextContent,
|
|
|
|
ToolbarItem,
|
|
|
|
} from "@patternfly/react-core";
|
|
|
|
import { QuestionCircleIcon } from "@patternfly/react-icons";
|
2023-05-03 13:51:02 +00:00
|
|
|
import { useState } from "react";
|
|
|
|
import { Trans, useTranslation } from "react-i18next";
|
|
|
|
import { Link } from "react-router-dom";
|
|
|
|
import { useHelp } from "ui-shared";
|
2022-02-09 11:46:58 +00:00
|
|
|
|
2023-05-03 13:51:02 +00:00
|
|
|
import { adminClient } from "../admin-client";
|
|
|
|
import { useAlerts } from "../components/alert/Alerts";
|
|
|
|
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
|
|
|
import { GroupPickerDialog } from "../components/group/GroupPickerDialog";
|
|
|
|
import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner";
|
|
|
|
import { ListEmptyState } from "../components/list-empty-state/ListEmptyState";
|
2023-03-21 12:05:17 +00:00
|
|
|
import {
|
|
|
|
Action,
|
|
|
|
KeycloakDataTable,
|
|
|
|
} from "../components/table-toolbar/KeycloakDataTable";
|
2022-02-09 11:46:58 +00:00
|
|
|
import { useRealm } from "../context/realm-context/RealmContext";
|
2023-05-03 13:51:02 +00:00
|
|
|
import { toUserFederation } from "../user-federation/routes/UserFederation";
|
2023-05-03 15:40:27 +00:00
|
|
|
import { useFetch } from "../utils/useFetch";
|
2023-05-03 13:51:02 +00:00
|
|
|
import useToggle from "../utils/useToggle";
|
2022-02-09 11:46:58 +00:00
|
|
|
|
|
|
|
export const DefaultsGroupsTab = () => {
|
|
|
|
const { t } = useTranslation("realm-settings");
|
|
|
|
|
|
|
|
const [isKebabOpen, toggleKebab] = useToggle();
|
|
|
|
const [isGroupPickerOpen, toggleGroupPicker] = useToggle();
|
|
|
|
const [defaultGroups, setDefaultGroups] = useState<GroupRepresentation[]>();
|
|
|
|
const [selectedRows, setSelectedRows] = useState<GroupRepresentation[]>([]);
|
|
|
|
|
|
|
|
const [key, setKey] = useState(0);
|
|
|
|
const [load, setLoad] = useState(0);
|
|
|
|
const reload = () => setLoad(load + 1);
|
|
|
|
|
|
|
|
const { realm } = useRealm();
|
|
|
|
const { addAlert, addError } = useAlerts();
|
|
|
|
const { enabled } = useHelp();
|
|
|
|
|
|
|
|
useFetch(
|
|
|
|
() => adminClient.realms.getDefaultGroups({ realm }),
|
|
|
|
(groups) => {
|
|
|
|
setDefaultGroups(groups);
|
|
|
|
setKey(key + 1);
|
|
|
|
},
|
|
|
|
[load]
|
|
|
|
);
|
|
|
|
|
|
|
|
const loader = () => Promise.resolve(defaultGroups!);
|
|
|
|
|
|
|
|
const removeGroup = async () => {
|
|
|
|
try {
|
|
|
|
await Promise.all(
|
|
|
|
selectedRows.map((group) =>
|
|
|
|
adminClient.realms.removeDefaultGroup({
|
|
|
|
realm,
|
|
|
|
id: group.id!,
|
|
|
|
})
|
|
|
|
)
|
|
|
|
);
|
|
|
|
addAlert(
|
|
|
|
t("groupRemove", { count: selectedRows.length }),
|
|
|
|
AlertVariant.success
|
|
|
|
);
|
|
|
|
setSelectedRows([]);
|
|
|
|
} catch (error) {
|
|
|
|
addError("realm-settings:groupRemoveError", error);
|
|
|
|
}
|
|
|
|
reload();
|
|
|
|
};
|
|
|
|
|
|
|
|
const addGroups = async (groups: GroupRepresentation[]) => {
|
|
|
|
try {
|
|
|
|
await Promise.all(
|
|
|
|
groups.map((group) =>
|
|
|
|
adminClient.realms.addDefaultGroup({
|
|
|
|
realm,
|
|
|
|
id: group.id!,
|
|
|
|
})
|
|
|
|
)
|
|
|
|
);
|
|
|
|
addAlert(
|
|
|
|
t("defaultGroupAdded", { count: groups.length }),
|
|
|
|
AlertVariant.success
|
|
|
|
);
|
|
|
|
} catch (error) {
|
|
|
|
addError("realm-settings:defaultGroupAddedError", error);
|
|
|
|
}
|
|
|
|
reload();
|
|
|
|
};
|
|
|
|
|
|
|
|
const [toggleRemoveDialog, RemoveDialog] = useConfirmDialog({
|
|
|
|
titleKey: t("removeConfirmTitle", { count: selectedRows.length }),
|
|
|
|
messageKey: t("removeConfirm", { count: selectedRows.length }),
|
|
|
|
continueButtonLabel: "common:delete",
|
|
|
|
continueButtonVariant: ButtonVariant.danger,
|
|
|
|
onConfirm: removeGroup,
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!defaultGroups) {
|
|
|
|
return <KeycloakSpinner />;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<RemoveDialog />
|
|
|
|
{isGroupPickerOpen && (
|
|
|
|
<GroupPickerDialog
|
|
|
|
type="selectMany"
|
|
|
|
text={{
|
|
|
|
title: "realm-settings:addDefaultGroups",
|
|
|
|
ok: "common:add",
|
|
|
|
}}
|
|
|
|
onConfirm={(groups) => {
|
2022-05-12 13:46:10 +00:00
|
|
|
addGroups(groups || []);
|
2022-02-09 11:46:58 +00:00
|
|
|
toggleGroupPicker();
|
|
|
|
}}
|
|
|
|
onClose={toggleGroupPicker}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
{enabled && (
|
|
|
|
<Popover
|
|
|
|
bodyContent={
|
|
|
|
<Trans i18nKey="realm-settings-help:defaultGroups">
|
|
|
|
{" "}
|
|
|
|
<Link to={toUserFederation({ realm })} />.
|
|
|
|
</Trans>
|
|
|
|
}
|
|
|
|
>
|
|
|
|
<TextContent
|
|
|
|
className="keycloak__section_intro__help"
|
|
|
|
style={{
|
|
|
|
paddingLeft: "var(--pf-c-page__main-section--PaddingLeft)",
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<Text>
|
|
|
|
<QuestionCircleIcon /> {t("whatIsDefaultGroups")}
|
|
|
|
</Text>
|
|
|
|
</TextContent>
|
|
|
|
</Popover>
|
|
|
|
)}
|
|
|
|
<KeycloakDataTable
|
|
|
|
key={key}
|
|
|
|
canSelectAll
|
|
|
|
onSelect={(rows) => setSelectedRows([...rows])}
|
|
|
|
loader={loader}
|
|
|
|
ariaLabelKey="realm-settings:defaultGroups"
|
|
|
|
searchPlaceholderKey="realm-settings:searchForGroups"
|
|
|
|
toolbarItem={
|
|
|
|
<>
|
|
|
|
<ToolbarItem>
|
|
|
|
<Button
|
|
|
|
data-testid="openCreateGroupModal"
|
|
|
|
variant="primary"
|
|
|
|
onClick={toggleGroupPicker}
|
|
|
|
>
|
|
|
|
{t("addGroups")}
|
|
|
|
</Button>
|
|
|
|
</ToolbarItem>
|
|
|
|
<ToolbarItem>
|
|
|
|
<Dropdown
|
|
|
|
toggle={
|
|
|
|
<KebabToggle
|
|
|
|
onToggle={toggleKebab}
|
|
|
|
isDisabled={selectedRows!.length === 0}
|
|
|
|
/>
|
|
|
|
}
|
|
|
|
isOpen={isKebabOpen}
|
|
|
|
isPlain
|
|
|
|
dropdownItems={[
|
|
|
|
<DropdownItem
|
|
|
|
key="action"
|
|
|
|
component="button"
|
|
|
|
onClick={() => {
|
|
|
|
toggleRemoveDialog();
|
|
|
|
toggleKebab();
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
{t("common:remove")}
|
|
|
|
</DropdownItem>,
|
|
|
|
]}
|
|
|
|
/>
|
|
|
|
</ToolbarItem>
|
|
|
|
</>
|
|
|
|
}
|
|
|
|
actions={[
|
|
|
|
{
|
|
|
|
title: t("common:remove"),
|
2023-03-21 12:05:17 +00:00
|
|
|
onRowClick: (group) => {
|
2022-02-09 11:46:58 +00:00
|
|
|
setSelectedRows([group]);
|
|
|
|
toggleRemoveDialog();
|
|
|
|
return Promise.resolve(false);
|
|
|
|
},
|
2023-03-21 12:05:17 +00:00
|
|
|
} as Action<GroupRepresentation>,
|
2022-02-09 11:46:58 +00:00
|
|
|
]}
|
|
|
|
columns={[
|
|
|
|
{
|
|
|
|
name: "name",
|
|
|
|
displayKey: "groups:groupName",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "path",
|
|
|
|
displayKey: "groups:path",
|
|
|
|
},
|
|
|
|
]}
|
|
|
|
emptyState={
|
|
|
|
<ListEmptyState
|
|
|
|
hasIcon
|
|
|
|
message={t("noDefaultGroups")}
|
|
|
|
instructions={
|
|
|
|
<Trans i18nKey="realm-settings:noDefaultGroupsInstructions">
|
|
|
|
{" "}
|
2023-03-28 16:47:26 +00:00
|
|
|
<Link
|
|
|
|
className="pf-u-font-weight-light"
|
|
|
|
to={toUserFederation({ realm })}
|
|
|
|
/>
|
2022-02-09 11:46:58 +00:00
|
|
|
Add groups...
|
|
|
|
</Trans>
|
|
|
|
}
|
|
|
|
primaryActionText={t("addGroups")}
|
|
|
|
onPrimaryAction={toggleGroupPicker}
|
|
|
|
/>
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
};
|