Realm-settings -> User Profile -> Attributes (#2107)
* attributes tab - wip * attributes tab - wip * attributes tab - wip * attributes tab - wip * added dropdown for each attribute row * added kebab logic * added delete dialog - wip * added delete dialog - wip * added delete dialog - wip * draggable rows - wip * draggable rows - wip * draggable rows - wip * refactored draggable rows * refactored draggable rows * added tests * added tests * refactor * refactor * refactor * renamed css file to realm-settings-section.css Co-authored-by: Agnieszka Gancarczyk <agancarc@redhat.com>
This commit is contained in:
parent
42d8c31e84
commit
a6904be9ff
18 changed files with 347 additions and 23 deletions
|
@ -1,5 +1,5 @@
|
||||||
import ListingPage from "../support/pages/admin_console/ListingPage";
|
import ListingPage from "../support/pages/admin_console/ListingPage";
|
||||||
import RealmSettingsPage from "../support/pages/admin_console/manage/realm_settings/RealmSettingsPage";
|
import UserProfile from "../support/pages/admin_console/manage/realm_settings/UserProfile";
|
||||||
import SidebarPage from "../support/pages/admin_console/SidebarPage";
|
import SidebarPage from "../support/pages/admin_console/SidebarPage";
|
||||||
import LoginPage from "../support/pages/LoginPage";
|
import LoginPage from "../support/pages/LoginPage";
|
||||||
import AdminClient from "../support/util/AdminClient";
|
import AdminClient from "../support/util/AdminClient";
|
||||||
|
@ -8,15 +8,18 @@ import ModalUtils from "../support/util/ModalUtils";
|
||||||
|
|
||||||
const loginPage = new LoginPage();
|
const loginPage = new LoginPage();
|
||||||
const sidebarPage = new SidebarPage();
|
const sidebarPage = new SidebarPage();
|
||||||
const realmSettingsPage = new RealmSettingsPage();
|
const userProfileTab = new UserProfile();
|
||||||
const adminClient = new AdminClient();
|
const adminClient = new AdminClient();
|
||||||
const listingPage = new ListingPage();
|
const listingPage = new ListingPage();
|
||||||
const modalUtils = new ModalUtils();
|
const modalUtils = new ModalUtils();
|
||||||
|
|
||||||
// Selectors
|
// Selectors
|
||||||
const getUserProfileTab = () =>
|
const getUserProfileTab = () => userProfileTab.goToTab();
|
||||||
cy.findByTestId(realmSettingsPage.userProfileTab);
|
const getAttributesTab = () => userProfileTab.goToAttributesTab();
|
||||||
const getAttributesGroupTab = () => cy.findByTestId("attributesGroupTab");
|
const getAttributesGroupTab = () => userProfileTab.goToAttributesGroupTab();
|
||||||
|
const getJsonEditorTab = () => userProfileTab.goToJsonEditorTab();
|
||||||
|
const clickCreateAttributeButton = () =>
|
||||||
|
userProfileTab.createAttributeButtonClick();
|
||||||
|
|
||||||
describe("User profile tabs", () => {
|
describe("User profile tabs", () => {
|
||||||
const realmName = "Realm_" + (Math.random() + 1).toString(36).substring(7);
|
const realmName = "Realm_" + (Math.random() + 1).toString(36).substring(7);
|
||||||
|
@ -36,19 +39,35 @@ describe("User profile tabs", () => {
|
||||||
sidebarPage.goToRealmSettings();
|
sidebarPage.goToRealmSettings();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Attribute groups", () => {
|
describe("Attributes sub tab tests", () => {
|
||||||
it("deletes an attributes group", () => {
|
it("Goes to create attribute page", () => {
|
||||||
|
getUserProfileTab();
|
||||||
|
getAttributesTab();
|
||||||
|
clickCreateAttributeButton();
|
||||||
|
cy.get("p").should("have.text", "Create attribute");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Attribute groups sub tab tests", () => {
|
||||||
|
it("Deletes an attributes group", () => {
|
||||||
cy.wrap(null).then(() =>
|
cy.wrap(null).then(() =>
|
||||||
adminClient.patchUserProfile(realmName, {
|
adminClient.patchUserProfile(realmName, {
|
||||||
groups: [{ name: "Test" }],
|
groups: [{ name: "Test" }],
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
getUserProfileTab().click();
|
getUserProfileTab();
|
||||||
getAttributesGroupTab().click();
|
getAttributesGroupTab();
|
||||||
listingPage.deleteItem("Test");
|
listingPage.deleteItem("Test");
|
||||||
modalUtils.confirmModal();
|
modalUtils.confirmModal();
|
||||||
listingPage.itemExist("Test", false);
|
listingPage.itemExist("Test", false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("Json Editor sub tab tests", () => {
|
||||||
|
it("Goes to Json Editor tab", () => {
|
||||||
|
getUserProfileTab();
|
||||||
|
getJsonEditorTab();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
|
@ -0,0 +1,50 @@
|
||||||
|
export default class UserProfile {
|
||||||
|
private userProfileTab = "rs-user-profile-tab";
|
||||||
|
private attributesTab = "attributesTab";
|
||||||
|
private attributesGroupTab = "attributesGroupTab";
|
||||||
|
private jsonEditorTab = "jsonEditorTab";
|
||||||
|
private createAttributeButton = "createAttributeBtn";
|
||||||
|
private actionsDrpDwn = "actions-dropdown";
|
||||||
|
private deleteDrpDwnOption = "deleteDropdownAttributeItem";
|
||||||
|
private editDrpDwnOption = "editDropdownAttributeItem";
|
||||||
|
|
||||||
|
goToTab() {
|
||||||
|
cy.findByTestId(this.userProfileTab).click();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
goToAttributesTab() {
|
||||||
|
cy.findByTestId(this.attributesTab).click();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
goToAttributesGroupTab() {
|
||||||
|
cy.findByTestId(this.attributesGroupTab).click();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
goToJsonEditorTab() {
|
||||||
|
cy.findByTestId(this.jsonEditorTab).click();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
createAttributeButtonClick() {
|
||||||
|
cy.findByTestId(this.createAttributeButton).click();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectDropdown() {
|
||||||
|
cy.findByTestId(this.actionsDrpDwn).click();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectDeleteOption() {
|
||||||
|
cy.findByTestId(this.deleteDrpDwnOption).click();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectEditOption() {
|
||||||
|
cy.findByTestId(this.editDrpDwnOption).click();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,7 +31,7 @@ import { useAdminClient, useFetch } from "../context/auth/AdminClient";
|
||||||
import type ClientProfileRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientProfileRepresentation";
|
import type ClientProfileRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientProfileRepresentation";
|
||||||
import { HelpItem } from "../components/help-enabler/HelpItem";
|
import { HelpItem } from "../components/help-enabler/HelpItem";
|
||||||
import { PlusCircleIcon, TrashIcon } from "@patternfly/react-icons";
|
import { PlusCircleIcon, TrashIcon } from "@patternfly/react-icons";
|
||||||
import "./RealmSettingsSection.css";
|
import "./realm-settings-section.css";
|
||||||
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
||||||
import { toAddExecutor } from "./routes/AddExecutor";
|
import { toAddExecutor } from "./routes/AddExecutor";
|
||||||
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
|
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
|
||||||
|
|
|
@ -21,7 +21,7 @@ import { useRealm } from "../context/realm-context/RealmContext";
|
||||||
import { useWhoAmI } from "../context/whoami/WhoAmI";
|
import { useWhoAmI } from "../context/whoami/WhoAmI";
|
||||||
import { emailRegexPattern } from "../util";
|
import { emailRegexPattern } from "../util";
|
||||||
import { AddUserEmailModal } from "./AddUserEmailModal";
|
import { AddUserEmailModal } from "./AddUserEmailModal";
|
||||||
import "./RealmSettingsSection.css";
|
import "./realm-settings-section.css";
|
||||||
|
|
||||||
type RealmSettingsEmailTabProps = {
|
type RealmSettingsEmailTabProps = {
|
||||||
realm: RealmRepresentation;
|
realm: RealmRepresentation;
|
||||||
|
|
|
@ -20,7 +20,7 @@ import { emptyFormatter } from "../util";
|
||||||
import { useAdminClient } from "../context/auth/AdminClient";
|
import { useAdminClient } from "../context/auth/AdminClient";
|
||||||
import { useRealm } from "../context/realm-context/RealmContext";
|
import { useRealm } from "../context/realm-context/RealmContext";
|
||||||
|
|
||||||
import "./RealmSettingsSection.css";
|
import "./realm-settings-section.css";
|
||||||
import { FilterIcon } from "@patternfly/react-icons";
|
import { FilterIcon } from "@patternfly/react-icons";
|
||||||
|
|
||||||
type KeyData = KeyMetadataRepresentation & {
|
type KeyData = KeyMetadataRepresentation & {
|
||||||
|
@ -72,7 +72,7 @@ export const KeysListTab = ({ realmComponents }: KeysListTabProps) => {
|
||||||
|
|
||||||
const activeKeysCopy = keys!.filter((i) => i.status === "ACTIVE");
|
const activeKeysCopy = keys!.filter((i) => i.status === "ACTIVE");
|
||||||
|
|
||||||
return activeKeysCopy?.map((key) => {
|
return activeKeysCopy.map((key) => {
|
||||||
const provider = realmComponents.find(
|
const provider = realmComponents.find(
|
||||||
(component: ComponentRepresentation) => component.id === key.providerId
|
(component: ComponentRepresentation) => component.id === key.providerId
|
||||||
);
|
);
|
||||||
|
@ -88,7 +88,7 @@ export const KeysListTab = ({ realmComponents }: KeysListTabProps) => {
|
||||||
|
|
||||||
const passiveKeys = keys!.filter((i) => i.status === "PASSIVE");
|
const passiveKeys = keys!.filter((i) => i.status === "PASSIVE");
|
||||||
|
|
||||||
return passiveKeys?.map((key) => {
|
return passiveKeys.map((key) => {
|
||||||
const provider = realmComponents.find(
|
const provider = realmComponents.find(
|
||||||
(component: ComponentRepresentation) => component.id === key.providerId
|
(component: ComponentRepresentation) => component.id === key.providerId
|
||||||
);
|
);
|
||||||
|
@ -104,7 +104,7 @@ export const KeysListTab = ({ realmComponents }: KeysListTabProps) => {
|
||||||
|
|
||||||
const disabledKeys = keys!.filter((i) => i.status === "DISABLED");
|
const disabledKeys = keys!.filter((i) => i.status === "DISABLED");
|
||||||
|
|
||||||
return disabledKeys?.map((key) => {
|
return disabledKeys.map((key) => {
|
||||||
const provider = realmComponents!.find(
|
const provider = realmComponents!.find(
|
||||||
(component: ComponentRepresentation) => component.id === key.providerId
|
(component: ComponentRepresentation) => component.id === key.providerId
|
||||||
);
|
);
|
||||||
|
|
|
@ -31,7 +31,7 @@ import type { KeyMetadataRepresentation } from "@keycloak/keycloak-admin-client/
|
||||||
import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation";
|
import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation";
|
||||||
import type ComponentTypeRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentTypeRepresentation";
|
import type ComponentTypeRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentTypeRepresentation";
|
||||||
|
|
||||||
import "./RealmSettingsSection.css";
|
import "./realm-settings-section.css";
|
||||||
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
|
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
|
||||||
import { useAdminClient } from "../context/auth/AdminClient";
|
import { useAdminClient } from "../context/auth/AdminClient";
|
||||||
import { useAlerts } from "../components/alert/Alerts";
|
import { useAlerts } from "../components/alert/Alerts";
|
||||||
|
@ -450,7 +450,7 @@ export const KeysProvidersTab = ({
|
||||||
}: KeysProps) => {
|
}: KeysProps) => {
|
||||||
return (
|
return (
|
||||||
<KeysTabInner
|
<KeysTabInner
|
||||||
components={realmComponents?.map((component) => {
|
components={realmComponents.map((component) => {
|
||||||
const provider = keyProviderComponentTypes.find(
|
const provider = keyProviderComponentTypes.find(
|
||||||
(componentType: ComponentTypeRepresentation) =>
|
(componentType: ComponentTypeRepresentation) =>
|
||||||
component.providerId === componentType.id
|
component.providerId === componentType.id
|
||||||
|
|
23
src/realm-settings/NewAttributeSettings.tsx
Normal file
23
src/realm-settings/NewAttributeSettings.tsx
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { PageSection } from "@patternfly/react-core";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { FormAccess } from "../components/form-access/FormAccess";
|
||||||
|
import "./realm-settings-section.css";
|
||||||
|
|
||||||
|
export default function NewAttributeSettings() {
|
||||||
|
const { t } = useTranslation("realm-settings");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PageSection variant="light">
|
||||||
|
<FormAccess
|
||||||
|
onSubmit={() => console.log("TODO handle submit")}
|
||||||
|
isHorizontal
|
||||||
|
role="view-realm"
|
||||||
|
className="pf-u-mt-lg"
|
||||||
|
>
|
||||||
|
<p>{t("createAttribute")}</p>
|
||||||
|
</FormAccess>
|
||||||
|
</PageSection>
|
||||||
|
);
|
||||||
|
}
|
|
@ -44,7 +44,7 @@ import { AddClientProfileModal } from "./AddClientProfileModal";
|
||||||
import type ClientProfileRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientProfileRepresentation";
|
import type ClientProfileRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientProfileRepresentation";
|
||||||
import { toClientPolicies } from "./routes/ClientPolicies";
|
import { toClientPolicies } from "./routes/ClientPolicies";
|
||||||
|
|
||||||
import "./RealmSettingsSection.css";
|
import "./realm-settings-section.css";
|
||||||
|
|
||||||
type NewClientPolicyForm = Required<ClientPolicyRepresentation>;
|
type NewClientPolicyForm = Required<ClientPolicyRepresentation>;
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ import type ClientPolicyRepresentation from "@keycloak/keycloak-admin-client/lib
|
||||||
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
||||||
import { useAlerts } from "../components/alert/Alerts";
|
import { useAlerts } from "../components/alert/Alerts";
|
||||||
|
|
||||||
import "./RealmSettingsSection.css";
|
import "./realm-settings-section.css";
|
||||||
import { useRealm } from "../context/realm-context/RealmContext";
|
import { useRealm } from "../context/realm-context/RealmContext";
|
||||||
import { toAddClientPolicy } from "./routes/AddClientPolicy";
|
import { toAddClientPolicy } from "./routes/AddClientPolicy";
|
||||||
import { toEditClientPolicy } from "./routes/EditClientPolicy";
|
import { toEditClientPolicy } from "./routes/EditClientPolicy";
|
||||||
|
|
|
@ -26,7 +26,7 @@ import type ClientProfileRepresentation from "@keycloak/keycloak-admin-client/li
|
||||||
import { toClientProfile } from "./routes/ClientProfile";
|
import { toClientProfile } from "./routes/ClientProfile";
|
||||||
import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner";
|
import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner";
|
||||||
|
|
||||||
import "./RealmSettingsSection.css";
|
import "./realm-settings-section.css";
|
||||||
|
|
||||||
type ClientProfile = ClientProfileRepresentation & {
|
type ClientProfile = ClientProfileRepresentation & {
|
||||||
global: boolean;
|
global: boolean;
|
||||||
|
|
|
@ -15,7 +15,7 @@ import { HelpItem } from "../components/help-enabler/HelpItem";
|
||||||
import { FormPanel } from "../components/scroll-form/FormPanel";
|
import { FormPanel } from "../components/scroll-form/FormPanel";
|
||||||
import { TimeSelector } from "../components/time-selector/TimeSelector";
|
import { TimeSelector } from "../components/time-selector/TimeSelector";
|
||||||
|
|
||||||
import "./RealmSettingsSection.css";
|
import "./realm-settings-section.css";
|
||||||
|
|
||||||
type RealmSettingsSessionsTabProps = {
|
type RealmSettingsSessionsTabProps = {
|
||||||
realm: RealmRepresentation;
|
realm: RealmRepresentation;
|
||||||
|
|
|
@ -23,7 +23,7 @@ import { TimeSelector } from "../components/time-selector/TimeSelector";
|
||||||
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
|
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
|
||||||
import { forHumans, interpolateTimespan } from "../util";
|
import { forHumans, interpolateTimespan } from "../util";
|
||||||
|
|
||||||
import "./RealmSettingsSection.css";
|
import "./realm-settings-section.css";
|
||||||
|
|
||||||
type RealmSettingsSessionsTabProps = {
|
type RealmSettingsSessionsTabProps = {
|
||||||
realm: RealmRepresentation;
|
realm: RealmRepresentation;
|
||||||
|
|
|
@ -367,6 +367,18 @@ export default {
|
||||||
updateMessageBundleSuccess: "Success! Message bundle updated.",
|
updateMessageBundleSuccess: "Success! Message bundle updated.",
|
||||||
updateMessageBundleError: "Error updating message bundle.",
|
updateMessageBundleError: "Error updating message bundle.",
|
||||||
addMessageBundleError: "Error creating message bundle, {{error}}",
|
addMessageBundleError: "Error creating message bundle, {{error}}",
|
||||||
|
attributeName: "Name",
|
||||||
|
attributeDisplayName: "Display name",
|
||||||
|
attributeGroup: "Attribute group",
|
||||||
|
updatedUserProfileSuccess: "User Profile configuration has been saved",
|
||||||
|
updatedUserProfileError: "User Profile configuration hasn't been saved",
|
||||||
|
createAttribute: "Create attribute",
|
||||||
|
attributesDropdown: "Attributes dropdown",
|
||||||
|
deleteAttributeConfirmTitle: "Delete attribute?",
|
||||||
|
deleteAttributeConfirm:
|
||||||
|
"Are you sure you want to permanently delete the attribute {{attributeName}}?",
|
||||||
|
deleteAttributeSuccess: "Attribute deleted",
|
||||||
|
deleteAttributeError: "",
|
||||||
eventType: "Event saved type",
|
eventType: "Event saved type",
|
||||||
searchEventType: "Search saved event type",
|
searchEventType: "Search saved event type",
|
||||||
addSavedTypes: "Add saved types",
|
addSavedTypes: "Add saved types",
|
||||||
|
|
|
@ -16,6 +16,7 @@ import { EditClientPolicyRoute } from "./routes/EditClientPolicy";
|
||||||
import { NewClientPolicyConditionRoute } from "./routes/AddCondition";
|
import { NewClientPolicyConditionRoute } from "./routes/AddCondition";
|
||||||
import { EditClientPolicyConditionRoute } from "./routes/EditCondition";
|
import { EditClientPolicyConditionRoute } from "./routes/EditCondition";
|
||||||
import { UserProfileRoute } from "./routes/UserProfile";
|
import { UserProfileRoute } from "./routes/UserProfile";
|
||||||
|
import { AddAttributeRoute } from "./routes/AddAttribute";
|
||||||
import { KeysRoute } from "./routes/KeysTab";
|
import { KeysRoute } from "./routes/KeysTab";
|
||||||
|
|
||||||
const routes: RouteDef[] = [
|
const routes: RouteDef[] = [
|
||||||
|
@ -37,6 +38,7 @@ const routes: RouteDef[] = [
|
||||||
NewClientPolicyConditionRoute,
|
NewClientPolicyConditionRoute,
|
||||||
EditClientPolicyConditionRoute,
|
EditClientPolicyConditionRoute,
|
||||||
UserProfileRoute,
|
UserProfileRoute,
|
||||||
|
AddAttributeRoute,
|
||||||
];
|
];
|
||||||
|
|
||||||
export default routes;
|
export default routes;
|
||||||
|
|
21
src/realm-settings/routes/AddAttribute.ts
Normal file
21
src/realm-settings/routes/AddAttribute.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import type { LocationDescriptorObject } from "history";
|
||||||
|
import { lazy } from "react";
|
||||||
|
import { generatePath } from "react-router-dom";
|
||||||
|
import type { RouteDef } from "../../route-config";
|
||||||
|
|
||||||
|
export type AddAttributeParams = {
|
||||||
|
realm: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AddAttributeRoute: RouteDef = {
|
||||||
|
path: "/:realm/realm-settings/userProfile/attributes/add-attribute",
|
||||||
|
component: lazy(() => import("../NewAttributeSettings")),
|
||||||
|
breadcrumb: (t) => t("realmSettings"),
|
||||||
|
access: "view-realm",
|
||||||
|
};
|
||||||
|
|
||||||
|
export const toAddAttribute = (
|
||||||
|
params: AddAttributeParams
|
||||||
|
): LocationDescriptorObject => ({
|
||||||
|
pathname: generatePath(AddAttributeRoute.path, params),
|
||||||
|
});
|
192
src/realm-settings/user-profile/AttributesTab.tsx
Normal file
192
src/realm-settings/user-profile/AttributesTab.tsx
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
import React, { Fragment, useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
ButtonVariant,
|
||||||
|
Divider,
|
||||||
|
Dropdown,
|
||||||
|
DropdownItem,
|
||||||
|
KebabToggle,
|
||||||
|
ToolbarItem,
|
||||||
|
} from "@patternfly/react-core";
|
||||||
|
import { KeycloakSpinner } from "../../components/keycloak-spinner/KeycloakSpinner";
|
||||||
|
import { DraggableTable } from "../../authentication/components/DraggableTable";
|
||||||
|
import type { UserProfileAttribute } from "@keycloak/keycloak-admin-client/lib/defs/userProfileConfig";
|
||||||
|
import { useHistory } from "react-router-dom";
|
||||||
|
import { toAddAttribute } from "../routes/AddAttribute";
|
||||||
|
import { useRealm } from "../../context/realm-context/RealmContext";
|
||||||
|
import { useUserProfile } from "./UserProfileContext";
|
||||||
|
import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog";
|
||||||
|
|
||||||
|
type movedAttributeType = UserProfileAttribute;
|
||||||
|
|
||||||
|
export const AttributesTab = () => {
|
||||||
|
const { config, save } = useUserProfile();
|
||||||
|
const { realm: realmName } = useRealm();
|
||||||
|
const { t } = useTranslation("realm-settings");
|
||||||
|
const history = useHistory();
|
||||||
|
const [attributeToDelete, setAttributeToDelete] =
|
||||||
|
useState<{ name: string }>();
|
||||||
|
const [kebabOpen, setKebabOpen] = useState({
|
||||||
|
status: false,
|
||||||
|
rowKey: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const executeMove = async (
|
||||||
|
attribute: UserProfileAttribute,
|
||||||
|
newIndex: number
|
||||||
|
) => {
|
||||||
|
const fromIndex = config?.attributes!.findIndex((attr) => {
|
||||||
|
return attr.name === attribute.name;
|
||||||
|
});
|
||||||
|
|
||||||
|
let movedAttribute: movedAttributeType = {};
|
||||||
|
movedAttribute = config?.attributes![fromIndex!]!;
|
||||||
|
config?.attributes!.splice(fromIndex!, 1);
|
||||||
|
config?.attributes!.splice(newIndex, 0, movedAttribute);
|
||||||
|
|
||||||
|
save(
|
||||||
|
{ attributes: config?.attributes! },
|
||||||
|
{
|
||||||
|
successMessageKey: "realm-settings:updatedUserProfileSuccess",
|
||||||
|
errorMessageKey: "realm-settings:updatedUserProfileError",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const goToCreate = () => history.push(toAddAttribute({ realm: realmName }));
|
||||||
|
|
||||||
|
const updatedAttributes = config?.attributes!.filter(
|
||||||
|
(attribute) => attribute.name !== attributeToDelete?.name
|
||||||
|
);
|
||||||
|
|
||||||
|
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
|
||||||
|
titleKey: t("deleteAttributeConfirmTitle"),
|
||||||
|
messageKey: t("deleteAttributeConfirm", {
|
||||||
|
attributeName: attributeToDelete?.name!,
|
||||||
|
}),
|
||||||
|
continueButtonLabel: t("common:delete"),
|
||||||
|
continueButtonVariant: ButtonVariant.danger,
|
||||||
|
onConfirm: async () => {
|
||||||
|
save(
|
||||||
|
{ attributes: updatedAttributes! },
|
||||||
|
{
|
||||||
|
successMessageKey: "realm-settings:deleteAttributeSuccess",
|
||||||
|
errorMessageKey: "realm-settings:deleteAttributeError",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
setAttributeToDelete({
|
||||||
|
name: "",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!config) {
|
||||||
|
return <KeycloakSpinner />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="pf-u-mt-md pf-u-mb-md pf-u-ml-md">
|
||||||
|
<ToolbarItem className="kc-toolbar-attributesTab">
|
||||||
|
<Button
|
||||||
|
data-testid="createAttributeBtn"
|
||||||
|
variant="primary"
|
||||||
|
onClick={goToCreate}
|
||||||
|
>
|
||||||
|
{t("createAttribute")}
|
||||||
|
</Button>
|
||||||
|
</ToolbarItem>
|
||||||
|
</div>
|
||||||
|
<Divider />
|
||||||
|
<DeleteConfirm />
|
||||||
|
<DraggableTable
|
||||||
|
keyField="name"
|
||||||
|
onDragFinish={async (nameDragged, items) => {
|
||||||
|
const keys = config.attributes!.map((e) => e.name);
|
||||||
|
const newIndex = items.indexOf(nameDragged);
|
||||||
|
const oldIndex = keys.indexOf(nameDragged);
|
||||||
|
const dragged = config.attributes![oldIndex];
|
||||||
|
if (!dragged.name) return;
|
||||||
|
|
||||||
|
executeMove(dragged, newIndex);
|
||||||
|
}}
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
name: "name",
|
||||||
|
displayKey: t("attributeName"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "displayName",
|
||||||
|
displayKey: t("attributeDisplayName"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "group",
|
||||||
|
displayKey: t("attributeGroup"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "",
|
||||||
|
displayKey: "",
|
||||||
|
cellRenderer: (row) => (
|
||||||
|
<Dropdown
|
||||||
|
id={`${row.name}`}
|
||||||
|
label={t("attributesDropdown")}
|
||||||
|
data-testid="actions-dropdown"
|
||||||
|
toggle={
|
||||||
|
<KebabToggle
|
||||||
|
onToggle={(status) =>
|
||||||
|
setKebabOpen({
|
||||||
|
status,
|
||||||
|
rowKey: row.name!,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
id={`toggle-${row.name}`}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
isOpen={kebabOpen.status && kebabOpen.rowKey === row.name}
|
||||||
|
isPlain
|
||||||
|
dropdownItems={[
|
||||||
|
<DropdownItem
|
||||||
|
key={`edit-dropdown-item-${row.name}`}
|
||||||
|
data-testid="editDropdownAttributeItem"
|
||||||
|
onClick={() => {
|
||||||
|
setKebabOpen({
|
||||||
|
status: false,
|
||||||
|
rowKey: row.name!,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("common:edit")}
|
||||||
|
</DropdownItem>,
|
||||||
|
<Fragment key={`delete-dropdown-${row.name}`}>
|
||||||
|
{row.name !== "email" && row.name !== "username"
|
||||||
|
? [
|
||||||
|
<DropdownItem
|
||||||
|
key={`delete-dropdown-item-${row.name}`}
|
||||||
|
data-testid="deleteDropdownAttributeItem"
|
||||||
|
onClick={() => {
|
||||||
|
toggleDeleteDialog();
|
||||||
|
setAttributeToDelete({
|
||||||
|
name: row.name!,
|
||||||
|
});
|
||||||
|
setKebabOpen({
|
||||||
|
status: false,
|
||||||
|
rowKey: row.name!,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("common:delete")}
|
||||||
|
</DropdownItem>,
|
||||||
|
]
|
||||||
|
: []}
|
||||||
|
</Fragment>,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
data={config.attributes!}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
|
@ -9,6 +9,7 @@ import {
|
||||||
import { useRealm } from "../../context/realm-context/RealmContext";
|
import { useRealm } from "../../context/realm-context/RealmContext";
|
||||||
import { toUserProfile } from "../routes/UserProfile";
|
import { toUserProfile } from "../routes/UserProfile";
|
||||||
import { AttributesGroupTab } from "./AttributesGroupTab";
|
import { AttributesGroupTab } from "./AttributesGroupTab";
|
||||||
|
import { AttributesTab } from "./AttributesTab";
|
||||||
import { JsonEditorTab } from "./JsonEditorTab";
|
import { JsonEditorTab } from "./JsonEditorTab";
|
||||||
import { UserProfileProvider } from "./UserProfileContext";
|
import { UserProfileProvider } from "./UserProfileContext";
|
||||||
|
|
||||||
|
@ -25,11 +26,14 @@ export const UserProfileTab = () => {
|
||||||
>
|
>
|
||||||
<Tab
|
<Tab
|
||||||
title={<TabTitleText>{t("attributes")}</TabTitleText>}
|
title={<TabTitleText>{t("attributes")}</TabTitleText>}
|
||||||
|
data-testid="attributesTab"
|
||||||
{...routableTab({
|
{...routableTab({
|
||||||
to: toUserProfile({ realm, tab: "attributes" }),
|
to: toUserProfile({ realm, tab: "attributes" }),
|
||||||
history,
|
history,
|
||||||
})}
|
})}
|
||||||
></Tab>
|
>
|
||||||
|
<AttributesTab />
|
||||||
|
</Tab>
|
||||||
<Tab
|
<Tab
|
||||||
title={<TabTitleText>{t("attributesGroup")}</TabTitleText>}
|
title={<TabTitleText>{t("attributesGroup")}</TabTitleText>}
|
||||||
data-testid="attributesGroupTab"
|
data-testid="attributesGroupTab"
|
||||||
|
@ -42,6 +46,7 @@ export const UserProfileTab = () => {
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab
|
<Tab
|
||||||
title={<TabTitleText>{t("jsonEditor")}</TabTitleText>}
|
title={<TabTitleText>{t("jsonEditor")}</TabTitleText>}
|
||||||
|
data-testid="jsonEditorTab"
|
||||||
{...routableTab({
|
{...routableTab({
|
||||||
to: toUserProfile({ realm, tab: "jsonEditor" }),
|
to: toUserProfile({ realm, tab: "jsonEditor" }),
|
||||||
history,
|
history,
|
||||||
|
|
Loading…
Reference in a new issue