Re-use User Profile logic from shared context (#1951)

This commit is contained in:
Jon Koops 2022-02-07 12:49:47 +01:00 committed by GitHub
parent 7dfa1a8b19
commit d85ff6e9be
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 121 additions and 111 deletions

View file

@ -1,4 +1,3 @@
import type UserProfileConfig from "@keycloak/keycloak-admin-client/lib/defs/userProfileConfig";
import type { UserProfileGroup } from "@keycloak/keycloak-admin-client/lib/defs/userProfileConfig";
import {
Button,
@ -12,17 +11,10 @@ import { Link, useHistory } from "react-router-dom";
import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog";
import { ListEmptyState } from "../../components/list-empty-state/ListEmptyState";
import { KeycloakDataTable } from "../../components/table-toolbar/KeycloakDataTable";
import type { OnSaveCallback } from "./UserProfileTab";
import { useUserProfile } from "./UserProfileContext";
type AttributesGroupTabProps = {
config?: UserProfileConfig;
onSave: OnSaveCallback;
};
export const AttributesGroupTab = ({
config,
onSave,
}: AttributesGroupTabProps) => {
export const AttributesGroupTab = () => {
const { config, save } = useUserProfile();
const { t } = useTranslation();
const history = useHistory();
const [key, setKey] = useState(0);
@ -50,7 +42,7 @@ export const AttributesGroupTab = ({
(group) => group !== groupToDelete
);
onSave(
save(
{ ...config, groups },
{
successMessageKey: "attributes-group:deleteSuccess",

View file

@ -1,4 +1,3 @@
import type UserProfileConfig from "@keycloak/keycloak-admin-client/lib/defs/userProfileConfig";
import { CodeEditor, Language } from "@patternfly/react-code-editor";
import { ActionGroup, Button, Form, PageSection } from "@patternfly/react-core";
import type { editor } from "monaco-editor";
@ -6,19 +5,10 @@ import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { useAlerts } from "../../components/alert/Alerts";
import { prettyPrintJSON } from "../../util";
import type { OnSaveCallback } from "./UserProfileTab";
import { useUserProfile } from "./UserProfileContext";
type JsonEditorTabProps = {
config?: UserProfileConfig;
onSave: OnSaveCallback;
isSaving: boolean;
};
export const JsonEditorTab = ({
config,
onSave,
isSaving,
}: JsonEditorTabProps) => {
export const JsonEditorTab = () => {
const { config, save, isSaving } = useUserProfile();
const { t } = useTranslation();
const { addError } = useAlerts();
const [editor, setEditor] = useState<editor.IStandaloneCodeEditor>();
@ -29,7 +19,7 @@ export const JsonEditorTab = ({
editor?.setValue(config ? prettyPrintJSON(config) : "");
}
function save() {
function handleSave() {
const value = editor?.getValue();
if (!value) {
@ -37,7 +27,7 @@ export const JsonEditorTab = ({
}
try {
onSave(JSON.parse(value));
save(JSON.parse(value));
} catch (error) {
addError("realm-settings:invalidJsonError", error);
return;
@ -54,7 +44,7 @@ export const JsonEditorTab = ({
/>
<Form>
<ActionGroup>
<Button variant="primary" onClick={save} isDisabled={isSaving}>
<Button variant="primary" onClick={handleSave} isDisabled={isSaving}>
{t("common:save")}
</Button>
<Button variant="link" onClick={resetCode} isDisabled={isSaving}>

View file

@ -0,0 +1,76 @@
import type UserProfileConfig from "@keycloak/keycloak-admin-client/lib/defs/userProfileConfig";
import { AlertVariant } from "@patternfly/react-core";
import React, { createContext, FunctionComponent, useState } from "react";
import { useTranslation } from "react-i18next";
import { useAlerts } from "../../components/alert/Alerts";
import { useAdminClient, useFetch } from "../../context/auth/AdminClient";
import { useRealm } from "../../context/realm-context/RealmContext";
import useRequiredContext from "../../utils/useRequiredContext";
type UserProfileProps = {
config: UserProfileConfig | null;
save: SaveCallback;
isSaving: boolean;
};
export type SaveCallback = (
updatedConfig: UserProfileConfig,
options?: SaveOptions
) => Promise<void>;
export type SaveOptions = {
successMessageKey?: string;
errorMessageKey?: string;
};
export const UserProfile = createContext<UserProfileProps | undefined>(
undefined
);
export const UserProfileProvider: FunctionComponent = ({ children }) => {
const adminClient = useAdminClient();
const { realm } = useRealm();
const { addAlert, addError } = useAlerts();
const { t } = useTranslation();
const [config, setConfig] = useState<UserProfileConfig | null>(null);
const [refreshCount, setRefreshCount] = useState(0);
const [isSaving, setIsSaving] = useState(false);
useFetch(
() => adminClient.users.getProfile({ realm }),
(config) => setConfig(config),
[refreshCount]
);
const save: SaveCallback = async (updatedConfig, options) => {
setIsSaving(true);
try {
await adminClient.users.updateProfile({
...updatedConfig,
realm,
});
setRefreshCount(refreshCount + 1);
addAlert(
t(options?.successMessageKey ?? "realm-settings:userProfileSuccess"),
AlertVariant.success
);
} catch (error) {
addError(
options?.errorMessageKey ?? "realm-settings:userProfileError",
error
);
}
setIsSaving(false);
};
return (
<UserProfile.Provider value={{ config, save, isSaving }}>
{children}
</UserProfile.Provider>
);
};
export const useUserProfile = () => useRequiredContext(UserProfile);

View file

@ -1,103 +1,55 @@
import type UserProfileConfig from "@keycloak/keycloak-admin-client/lib/defs/userProfileConfig";
import { AlertVariant, Tab, TabTitleText } from "@patternfly/react-core";
import React, { useState } from "react";
import { Tab, TabTitleText } from "@patternfly/react-core";
import React from "react";
import { useTranslation } from "react-i18next";
import { useHistory } from "react-router-dom";
import { useAlerts } from "../../components/alert/Alerts";
import {
routableTab,
RoutableTabs,
} from "../../components/routable-tabs/RoutableTabs";
import { useAdminClient, useFetch } from "../../context/auth/AdminClient";
import { useRealm } from "../../context/realm-context/RealmContext";
import { toUserProfile } from "../routes/UserProfile";
import { AttributesGroupTab } from "./AttributesGroupTab";
import { JsonEditorTab } from "./JsonEditorTab";
export type OnSaveCallback = (
updatedProfiles: UserProfileConfig,
options?: OnSaveOptions
) => Promise<void>;
export type OnSaveOptions = {
successMessageKey?: string;
errorMessageKey?: string;
};
import { UserProfileProvider } from "./UserProfileContext";
export const UserProfileTab = () => {
const adminClient = useAdminClient();
const { realm } = useRealm();
const { t } = useTranslation("realm-settings");
const history = useHistory();
const { addAlert, addError } = useAlerts();
const [config, setConfig] = useState<UserProfileConfig>();
const [isSaving, setIsSaving] = useState(false);
const [refreshCount, setRefreshCount] = useState(0);
useFetch(
() => adminClient.users.getProfile({ realm }),
(config) => setConfig(config),
[refreshCount]
);
const onSave: OnSaveCallback = async (
updatedProfiles: UserProfileConfig,
options?: OnSaveOptions
) => {
setIsSaving(true);
try {
await adminClient.users.updateProfile({
...updatedProfiles,
realm,
});
setRefreshCount(refreshCount + 1);
addAlert(
t(options?.successMessageKey ?? "userProfileSuccess"),
AlertVariant.success
);
} catch (error) {
addError(
options?.errorMessageKey ?? "realm-settings:userProfileError",
error
);
}
setIsSaving(false);
};
return (
<RoutableTabs
defaultLocation={toUserProfile({ realm, tab: "attributes" })}
mountOnEnter
>
<Tab
title={<TabTitleText>{t("attributes")}</TabTitleText>}
{...routableTab({
to: toUserProfile({ realm, tab: "attributes" }),
history,
})}
></Tab>
<Tab
title={<TabTitleText>{t("attributesGroup")}</TabTitleText>}
data-testid="attributesGroupTab"
{...routableTab({
to: toUserProfile({ realm, tab: "attributesGroup" }),
history,
})}
<UserProfileProvider>
<RoutableTabs
defaultLocation={toUserProfile({ realm, tab: "attributes" })}
mountOnEnter
>
<AttributesGroupTab config={config} onSave={onSave} />
</Tab>
<Tab
title={<TabTitleText>{t("jsonEditor")}</TabTitleText>}
{...routableTab({
to: toUserProfile({ realm, tab: "jsonEditor" }),
history,
})}
>
<JsonEditorTab config={config} onSave={onSave} isSaving={isSaving} />
</Tab>
</RoutableTabs>
<Tab
title={<TabTitleText>{t("attributes")}</TabTitleText>}
{...routableTab({
to: toUserProfile({ realm, tab: "attributes" }),
history,
})}
></Tab>
<Tab
title={<TabTitleText>{t("attributesGroup")}</TabTitleText>}
data-testid="attributesGroupTab"
{...routableTab({
to: toUserProfile({ realm, tab: "attributesGroup" }),
history,
})}
>
<AttributesGroupTab />
</Tab>
<Tab
title={<TabTitleText>{t("jsonEditor")}</TabTitleText>}
{...routableTab({
to: toUserProfile({ realm, tab: "jsonEditor" }),
history,
})}
>
<JsonEditorTab />
</Tab>
</RoutableTabs>
</UserProfileProvider>
);
};