parent
c3c544b907
commit
2f5040f565
7 changed files with 78 additions and 6 deletions
|
@ -115,6 +115,7 @@
|
|||
"resourceSharedWith_one": "Resource is shared with <0>{{username}}</0>",
|
||||
"resourceSharedWith_other": "Resource is shared with <0>{{username}}</0> and <1>{{other}}</1> other users",
|
||||
"resourceSharedWith_zero": "This resource is not shared.",
|
||||
"selectALocale": "Select a locale",
|
||||
"selectOne": "Select an option",
|
||||
"setUpNew": "Set up {{0}}",
|
||||
"share": "Share",
|
||||
|
|
|
@ -29,6 +29,13 @@ export async function getPersonalInfo({
|
|||
return parseResponse<UserRepresentation>(response);
|
||||
}
|
||||
|
||||
export async function supportedLocales({ signal }: CallOptions = {}): Promise<
|
||||
string[]
|
||||
> {
|
||||
const response = await request("/supportedLocales", { signal });
|
||||
return parseResponse<string[]>(response);
|
||||
}
|
||||
|
||||
export async function savePersonalInfo(
|
||||
info: UserRepresentation
|
||||
): Promise<void> {
|
||||
|
|
|
@ -6,6 +6,7 @@ import { useTranslation } from "react-i18next";
|
|||
import { KeycloakTextInput } from "ui-shared";
|
||||
import { UserProfileAttributeMetadata } from "../api/representations";
|
||||
import { TFuncKey } from "../i18n";
|
||||
import { LocaleSelector } from "./LocaleSelector";
|
||||
import { fieldName, isBundleKey, unWrap } from "./PersonalInfo";
|
||||
|
||||
type FormFieldProps = {
|
||||
|
@ -25,6 +26,7 @@ export const FormField = ({ attribute }: FormFieldProps) => {
|
|||
const isSelect = (attribute: UserProfileAttributeMetadata) =>
|
||||
Object.hasOwn(attribute.validators, "options");
|
||||
|
||||
if (attribute.name === "locale") return <LocaleSelector />;
|
||||
return (
|
||||
<FormGroup
|
||||
key={attribute.name}
|
||||
|
|
43
js/apps/account-ui/src/personal-info/LocaleSelector.tsx
Normal file
43
js/apps/account-ui/src/personal-info/LocaleSelector.tsx
Normal file
|
@ -0,0 +1,43 @@
|
|||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import type { Option } from "ui-shared";
|
||||
import { SelectControl } from "ui-shared";
|
||||
import { supportedLocales } from "../api/methods";
|
||||
import { usePromise } from "../utils/usePromise";
|
||||
|
||||
const localeToDisplayName = (locale: string) => {
|
||||
try {
|
||||
return new Intl.DisplayNames([locale], { type: "language" }).of(locale);
|
||||
} catch (error) {
|
||||
return locale;
|
||||
}
|
||||
};
|
||||
|
||||
export const LocaleSelector = () => {
|
||||
const { t } = useTranslation();
|
||||
const [locales, setLocales] = useState<Option[]>([]);
|
||||
|
||||
usePromise(
|
||||
(signal) => supportedLocales({ signal }),
|
||||
(locales) =>
|
||||
setLocales(
|
||||
locales.map(
|
||||
(l) =>
|
||||
({
|
||||
key: l,
|
||||
value: localeToDisplayName(l),
|
||||
} as Option)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
return (
|
||||
<SelectControl
|
||||
id="locale-select"
|
||||
name="attributes.locale"
|
||||
label={t("selectALocale")}
|
||||
controller={{ defaultValue: "" }}
|
||||
options={locales}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -15,7 +15,7 @@ import {
|
|||
} from "@patternfly/react-core";
|
||||
import { FormLabel } from "./FormLabel";
|
||||
|
||||
type Option = {
|
||||
export type Option = {
|
||||
key: string;
|
||||
value: string;
|
||||
};
|
||||
|
@ -42,6 +42,7 @@ export const SelectControl = <
|
|||
label,
|
||||
options,
|
||||
controller,
|
||||
variant,
|
||||
...rest
|
||||
}: SelectControlProps<T, P>) => {
|
||||
const {
|
||||
|
@ -65,20 +66,31 @@ export const SelectControl = <
|
|||
{...rest}
|
||||
toggleId={name}
|
||||
onToggle={(isOpen) => setOpen(isOpen)}
|
||||
selections={value}
|
||||
selections={
|
||||
typeof options[0] !== "string"
|
||||
? (options as Option[]).find((o) => o.key === value[0])
|
||||
?.value || value
|
||||
: value
|
||||
}
|
||||
onSelect={(_, v) => {
|
||||
if (variant === "typeaheadmulti") {
|
||||
const option = v.toString();
|
||||
if (value.includes(option)) {
|
||||
onChange(value.filter((item: string) => item !== option));
|
||||
} else {
|
||||
onChange([...value, option]);
|
||||
}
|
||||
} else {
|
||||
onChange([v]);
|
||||
setOpen(false);
|
||||
}
|
||||
}}
|
||||
onClear={(event) => {
|
||||
event.stopPropagation();
|
||||
onChange([]);
|
||||
}}
|
||||
isOpen={open}
|
||||
variant={variant}
|
||||
validated={
|
||||
errors[name] ? ValidatedOptions.error : ValidatedOptions.default
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
export { ContinueCancelModal } from "./continue-cancel/ContinueCancelModal";
|
||||
export { SelectControl } from "./controls/SelectControl";
|
||||
export type { Option } from "./controls/SelectControl";
|
||||
export { SwitchControl } from "./controls/SwitchControl";
|
||||
export { TextControl } from "./controls/TextControl";
|
||||
export { TextAreaControl } from "./controls/TextAreaControl";
|
||||
|
|
|
@ -284,6 +284,12 @@ public class AccountRestService {
|
|||
return new ResourcesService(session, user, auth, request);
|
||||
}
|
||||
|
||||
@Path("supportedLocales")
|
||||
@GET
|
||||
public List<String> supportedLocales() {
|
||||
return auth.getRealm().getSupportedLocalesStream().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private ClientRepresentation modelToRepresentation(ClientModel model, List<String> inUseClients, List<String> offlineClients, Map<String, UserConsentModel> consents) {
|
||||
ClientRepresentation representation = new ClientRepresentation();
|
||||
representation.setClientId(model.getClientId());
|
||||
|
|
Loading…
Reference in a new issue