@keycloakify/keycloak-admin-ui
Version:
Repackaged Keycloak Admin UI
301 lines • 19.2 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { getI18n } from "react-i18next";
import { HelpItem, TextControl, useAlerts, useFetch, } from "../../ui-shared";
import { ActionGroup, Alert, Button, FormGroup, Grid, GridItem, PageSection, Text, TextContent, TextInput, } from "@patternfly/react-core";
import { GlobeRouteIcon } from "@patternfly/react-icons";
import { useEffect, useMemo, useState } from "react";
import { FormProvider, useForm, useWatch, } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { Link, useNavigate, useParams } from "react-router-dom";
import { useAdminClient } from "../../admin-client";
import { FormAccess } from "../../components/form/FormAccess";
import { KeyValueInput } from "../../components/key-value-form/KeyValueInput";
import { ViewHeader } from "../../components/view-header/ViewHeader";
import { useRealm } from "../../context/realm-context/RealmContext";
import useLocale from "../../utils/useLocale";
import useToggle from "../../utils/useToggle";
import "../realm-settings-section.css";
import { toUserProfile } from "../../realm-settings/routes/UserProfile";
import { useUserProfile } from "../../realm-settings/user-profile/UserProfileContext";
import { AddTranslationsDialog, } from "../../realm-settings/user-profile/attribute/AddTranslationsDialog";
function parseAnnotations(input) {
return Object.entries(input).reduce((p, [key, value]) => {
if (typeof value === "string") {
return [...p, { key, value }];
}
else {
return [...p];
}
}, []);
}
function transformAnnotations(input) {
return Object.fromEntries(input
.filter((annotation) => annotation.key.length > 0)
.map((annotation) => [annotation.key, annotation.value]));
}
const defaultValues = {
annotations: [],
displayDescription: "",
displayHeader: "",
name: "",
};
export default function AttributesGroupForm() {
const { adminClient } = useAdminClient();
const { t } = useTranslation();
const { realm: realmName, realmRepresentation: realm } = useRealm();
const { config, save } = useUserProfile();
const navigate = useNavigate();
const combinedLocales = useLocale();
const params = useParams();
const form = useForm({ defaultValues });
const { addError } = useAlerts();
const editMode = params.name ? true : false;
const [newAttributesGroupName, setNewAttributesGroupName] = useState("");
const [generatedAttributesGroupDisplayName, setGeneratedAttributesGroupDisplayName,] = useState("");
const [generatedAttributesGroupDisplayDescription, setGeneratedAttributesGroupDisplayDescription,] = useState("");
const [addTranslationsModalOpen, toggleModal] = useToggle();
const regexPattern = /\$\{([^}]+)\}/;
const [type, setType] = useState();
const [translationsData, setTranslationsData] = useState({
displayHeader: {
key: "",
translations: [],
},
displayDescription: {
key: "",
translations: [],
},
});
const matchingGroup = useMemo(() => { var _a; return (_a = config === null || config === void 0 ? void 0 : config.groups) === null || _a === void 0 ? void 0 : _a.find(({ name }) => name === params.name); }, [config === null || config === void 0 ? void 0 : config.groups]);
useEffect(() => {
if (!matchingGroup) {
return;
}
const annotations = matchingGroup.annotations
? parseAnnotations(matchingGroup.annotations)
: [];
form.reset({ ...defaultValues, ...matchingGroup, annotations });
}, [matchingGroup]);
useEffect(() => {
form.setValue("displayHeader", matchingGroup
? matchingGroup.displayHeader
: generatedAttributesGroupDisplayName);
form.setValue("displayDescription", matchingGroup
? matchingGroup.displayDescription
: generatedAttributesGroupDisplayDescription);
}, [
generatedAttributesGroupDisplayName,
generatedAttributesGroupDisplayDescription,
]);
useFetch(async () => {
const translationsToSaveDisplayHeader = [];
const translationsToSaveDisplayDescription = [];
const formData = form.getValues();
const translationsResults = await Promise.all(combinedLocales.map(async (selectedLocale) => {
var _a, _b, _c, _d;
try {
const translations = await adminClient.realms.getRealmLocalizationTexts({
realm: realmName,
selectedLocale,
});
const formattedDisplayHeaderKey = (_a = formData.displayHeader) === null || _a === void 0 ? void 0 : _a.substring(2, formData.displayHeader.length - 1);
const formattedDisplayDescriptionKey = (_b = formData.displayDescription) === null || _b === void 0 ? void 0 : _b.substring(2, formData.displayDescription.length - 1);
return {
locale: selectedLocale,
headerTranslation: (_c = translations[formattedDisplayHeaderKey]) !== null && _c !== void 0 ? _c : "",
descriptionTranslation: (_d = translations[formattedDisplayDescriptionKey]) !== null && _d !== void 0 ? _d : "",
};
}
catch (error) {
console.error(`Error fetching translations for ${selectedLocale}:`, error);
return null;
}
}));
translationsResults.forEach((translationsResult) => {
var _a, _b;
if (translationsResult) {
const { locale, headerTranslation, descriptionTranslation } = translationsResult;
translationsToSaveDisplayHeader.push({
key: (_a = formData.displayHeader) === null || _a === void 0 ? void 0 : _a.substring(2, formData.displayHeader.length - 1),
translations: [
{
locale,
value: headerTranslation,
},
],
});
translationsToSaveDisplayDescription.push({
key: (_b = formData.displayDescription) === null || _b === void 0 ? void 0 : _b.substring(2, formData.displayDescription.length - 1),
translations: [
{
locale,
value: descriptionTranslation,
},
],
});
}
});
return {
translationsToSaveDisplayHeader,
translationsToSaveDisplayDescription,
};
}, (data) => {
setTranslationsData({
displayHeader: {
key: data.translationsToSaveDisplayHeader[0].key,
translations: data.translationsToSaveDisplayHeader.flatMap((translationData) => translationData.translations),
},
displayDescription: {
key: data.translationsToSaveDisplayDescription[0].key,
translations: data.translationsToSaveDisplayDescription.flatMap((translationData) => translationData.translations),
},
});
}, [combinedLocales]);
const saveTranslations = async () => {
const addLocalization = async (key, locale, value) => {
try {
await adminClient.realms.addLocalization({
realm: realmName,
selectedLocale: locale,
key: key,
}, value);
}
catch (error) {
console.error(`Error saving translation for locale ${locale}: ${error}`);
}
};
try {
if (translationsData.displayHeader &&
translationsData.displayHeader.translations.length > 0) {
for (const translation of translationsData.displayHeader.translations) {
await addLocalization(translationsData.displayHeader.key, translation.locale, translation.value);
}
}
if (translationsData.displayDescription &&
translationsData.displayDescription.translations.length > 0) {
for (const translation of translationsData.displayDescription
.translations) {
await addLocalization(translationsData.displayDescription.key, translation.locale, translation.value);
}
}
}
catch (error) {
console.error(`Error while processing translations: ${error}`);
}
};
const onSubmit = async (values) => {
var _a;
if (!config) {
return;
}
const groups = [...((_a = config.groups) !== null && _a !== void 0 ? _a : [])];
const updateAt = matchingGroup ? groups.indexOf(matchingGroup) : -1;
const updatedGroup = {
...values,
annotations: transformAnnotations(values.annotations),
};
if (updateAt === -1) {
groups.push(updatedGroup);
}
else {
groups[updateAt] = updatedGroup;
}
if (realm === null || realm === void 0 ? void 0 : realm.internationalizationEnabled) {
const hasNonEmptyDisplayHeaderTranslations = translationsData.displayHeader.translations.some((translation) => translation.value.trim() !== "");
const hasNonEmptyDisplayDescriptionTranslations = translationsData.displayDescription.translations.some((translation) => translation.value.trim() !== "");
if (!hasNonEmptyDisplayHeaderTranslations ||
!hasNonEmptyDisplayDescriptionTranslations) {
addError("createAttributeError", t("translationError"));
return;
}
}
const success = await save({ ...config, groups });
if (success) {
await saveTranslations();
getI18n().reloadResources();
navigate(toUserProfile({ realm: realmName, tab: "attributes-group" }));
}
};
const attributesGroupDisplayName = useWatch({
control: form.control,
name: "displayHeader",
});
const attributesGroupDisplayDescription = useWatch({
control: form.control,
name: "displayDescription",
});
const handleAttributesGroupNameChange = (event, value) => {
const newDisplayName = value !== "" && (realm === null || realm === void 0 ? void 0 : realm.internationalizationEnabled)
? "${profile.attribute-group." + `${value}}`
: "";
const newDisplayDescription = value !== "" && (realm === null || realm === void 0 ? void 0 : realm.internationalizationEnabled)
? "${profile.attribute-group-description." + `${value}}`
: "";
setNewAttributesGroupName(value);
setGeneratedAttributesGroupDisplayName(newDisplayName);
setGeneratedAttributesGroupDisplayDescription(newDisplayDescription);
};
const attributesGroupDisplayPatternMatch = regexPattern.test(attributesGroupDisplayName || attributesGroupDisplayDescription);
const formattedAttributesGroupDisplayName = attributesGroupDisplayName === null || attributesGroupDisplayName === void 0 ? void 0 : attributesGroupDisplayName.substring(2, attributesGroupDisplayName.length - 1);
const formattedAttributesGroupDisplayDescription = attributesGroupDisplayDescription === null || attributesGroupDisplayDescription === void 0 ? void 0 : attributesGroupDisplayDescription.substring(2, attributesGroupDisplayDescription.length - 1);
const handleHeaderTranslationsAdded = (headerTranslations) => {
setTranslationsData((prev) => ({
...prev,
displayHeader: headerTranslations,
}));
};
const handleDescriptionTranslationsAdded = (descriptionTranslations) => {
setTranslationsData((prev) => ({
...prev,
displayDescription: descriptionTranslations,
}));
};
const handleToggleDialog = () => {
toggleModal();
};
const groupDisplayNameKey = type === "displayHeader"
? formattedAttributesGroupDisplayName
: `profile.attribute-group.${newAttributesGroupName}`;
const groupDisplayDescriptionKey = type === "displayDescription"
? formattedAttributesGroupDisplayDescription
: `profile.attribute-group-description.${newAttributesGroupName}`;
return (_jsxs(_Fragment, { children: [addTranslationsModalOpen && (_jsx(AddTranslationsDialog, { translationKey: type === "displayHeader"
? groupDisplayNameKey
: groupDisplayDescriptionKey, type: type === "displayHeader" ? "displayHeader" : "displayDescription", translations: type === "displayHeader"
? translationsData.displayHeader
: translationsData.displayDescription, onTranslationsAdded: type === "displayHeader"
? handleHeaderTranslationsAdded
: handleDescriptionTranslationsAdded, toggleDialog: handleToggleDialog, onCancel: () => {
toggleModal();
} })), _jsx(ViewHeader, { titleKey: matchingGroup ? "editGroupText" : "createGroupText", divider: true }), _jsx(PageSection, { variant: "light", onSubmit: form.handleSubmit(onSubmit), children: _jsx(FormAccess, { isHorizontal: true, role: "manage-realm", children: _jsxs(FormProvider, { ...form, children: [_jsx(TextControl, { name: "name", label: t("nameField"), labelIcon: t("nameHintHelp"), isDisabled: !!matchingGroup || editMode, rules: {
required: {
value: true,
message: t("required"),
},
onChange: (event) => {
handleAttributesGroupNameChange(event, event.target.value);
},
} }), !!matchingGroup && (_jsx("input", { type: "hidden", ...form.register("name") })), _jsx(FormGroup, { label: t("displayHeaderField"), labelIcon: _jsx(HelpItem, { helpText: t("displayHeaderHintHelp"), fieldLabelId: "displayHeaderField" }), fieldId: "kc-attributes-group-display-header", children: _jsxs(Grid, { hasGutter: true, children: [_jsxs(GridItem, { span: (realm === null || realm === void 0 ? void 0 : realm.internationalizationEnabled) ? 11 : 12, children: [_jsx(TextInput, { id: "kc-attributes-group-display-header", "data-testid": "attributes-group-display-header", isDisabled: ((realm === null || realm === void 0 ? void 0 : realm.internationalizationEnabled) &&
newAttributesGroupName !== "") ||
(editMode && attributesGroupDisplayPatternMatch), value: editMode
? attributesGroupDisplayName
: (realm === null || realm === void 0 ? void 0 : realm.internationalizationEnabled)
? generatedAttributesGroupDisplayName
: undefined, ...form.register("displayHeader") }), generatedAttributesGroupDisplayName && (_jsx(Alert, { className: "pf-v5-u-mt-sm", variant: "info", isInline: true, isPlain: true, title: t("addAttributesGroupTranslationInfo") }))] }), (realm === null || realm === void 0 ? void 0 : realm.internationalizationEnabled) && (_jsx(GridItem, { span: 1, children: _jsx(Button, { variant: "link", className: "pf-m-plain", "data-testid": "addAttributeDisplayNameTranslationBtn", "aria-label": t("addAttributeDisplayNameTranslation"), isDisabled: !newAttributesGroupName && !editMode, onClick: () => {
setType("displayHeader");
toggleModal();
}, icon: _jsx(GlobeRouteIcon, {}) }) }))] }) }), _jsx(FormGroup, { label: t("displayDescriptionField"), labelIcon: _jsx(HelpItem, { helpText: t("displayDescriptionHintHelp"), fieldLabelId: "displayDescriptionField" }), fieldId: "kc-attributes-group-display-description", children: _jsxs(Grid, { hasGutter: true, children: [_jsxs(GridItem, { span: (realm === null || realm === void 0 ? void 0 : realm.internationalizationEnabled) ? 11 : 12, children: [_jsx(TextInput, { id: "kc-attributes-group-display-description", "data-testid": "attributes-group-display-description", isDisabled: ((realm === null || realm === void 0 ? void 0 : realm.internationalizationEnabled) &&
newAttributesGroupName !== "") ||
(editMode && attributesGroupDisplayPatternMatch), value: editMode
? attributesGroupDisplayDescription
: (realm === null || realm === void 0 ? void 0 : realm.internationalizationEnabled)
? generatedAttributesGroupDisplayDescription
: undefined, ...form.register("displayDescription") }), generatedAttributesGroupDisplayDescription && (_jsx(Alert, { className: "pf-v5-u-mt-sm", variant: "info", isInline: true, isPlain: true, title: t("addAttributesGroupTranslationInfo") }))] }), (realm === null || realm === void 0 ? void 0 : realm.internationalizationEnabled) && (_jsx(GridItem, { span: 1, children: _jsx(Button, { variant: "link", className: "pf-m-plain", "data-testid": "addAttributeDisplayDescriptionTranslationBtn", "aria-label": t("addAttributeDisplayDescriptionTranslation"), isDisabled: !newAttributesGroupName && !editMode, onClick: () => {
setType("displayDescription");
toggleModal();
}, icon: _jsx(GlobeRouteIcon, {}) }) }))] }) }), _jsx(TextContent, { children: _jsx(Text, { component: "h2", children: t("annotationsText") }) }), _jsx(FormGroup, { label: t("annotationsText"), fieldId: "kc-annotations", children: _jsx(KeyValueInput, { label: t("annotationsText"), name: "annotations" }) }), _jsxs(ActionGroup, { children: [_jsx(Button, { variant: "primary", type: "submit", "data-testid": "saveGroupBtn", children: t("save") }), _jsx(Button, { variant: "link", component: (props) => (_jsx(Link, { ...props, to: toUserProfile({
realm: realmName,
tab: "attributes-group",
}) })), children: t("cancel") })] })] }) }) })] }));
}
//# sourceMappingURL=AttributesGroupForm.js.map