Re-use User Profile logic from shared context (#1951)
This commit is contained in:
parent
7dfa1a8b19
commit
d85ff6e9be
4 changed files with 121 additions and 111 deletions
|
@ -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",
|
||||
|
|
|
@ -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}>
|
||||
|
|
76
src/realm-settings/user-profile/UserProfileContext.tsx
Normal file
76
src/realm-settings/user-profile/UserProfileContext.tsx
Normal 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);
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue