Localization > Effective message bundles tab improvements (#26302)

* added a couple of improvements

Signed-off-by: Agnieszka Gancarczyk <agancarc@redhat.com>

* fix theme and themeType

Signed-off-by: Agnieszka Gancarczyk <agancarc@redhat.com>

* improved hasWords filter

Signed-off-by: Agnieszka Gancarczyk <agancarc@redhat.com>

* added refresh btn

Signed-off-by: Agnieszka Gancarczyk <agancarc@redhat.com>

* fixed backend

Signed-off-by: Agnieszka Gancarczyk <agancarc@redhat.com>

* added validation

Signed-off-by: Agnieszka Gancarczyk <agancarc@redhat.com>

* cleanup

Signed-off-by: Agnieszka Gancarczyk <agancarc@redhat.com>

* replaced sort with localSort

Signed-off-by: Agnieszka Gancarczyk <agancarc@redhat.com>

* improved spacing

Signed-off-by: Agnieszka Gancarczyk <agancarc@redhat.com>

* added useMemo

Signed-off-by: Agnieszka Gancarczyk <agancarc@redhat.com>

* removed filter on themeTypes

Signed-off-by: Agnieszka Gancarczyk <agancarc@redhat.com>

---------

Signed-off-by: Agnieszka Gancarczyk <agancarc@redhat.com>
Co-authored-by: Agnieszka Gancarczyk <agancarc@redhat.com>
This commit is contained in:
agagancarczyk 2024-01-19 12:21:29 +00:00 committed by GitHub
parent 84b2c5eb61
commit 229cbb5429
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 80 additions and 25 deletions

View file

@ -2985,4 +2985,5 @@ readBlog=Read blog
customValue=Custom value
termsAndConditionsUserAttribute=Terms and conditions accepted timestamp
realmOverridesDescription= Realm overrides allow you to specify translations that will take effect for the entire realm. These translations will override any translation specified by a theme.
addTranslation=Add translation
addTranslation=Add translation
effectiveMessageBundlesDescription=An effective message bundle is the set of translations for a given language, theme, and theme type. It also takes into account any realm overrides, which will take precedence.

View file

@ -13,9 +13,12 @@ import {
Select,
SelectOption,
SelectVariant,
Text,
TextContent,
TextVariants,
} from "@patternfly/react-core";
import { pickBy } from "lodash-es";
import { useState } from "react";
import { useMemo, useState } from "react";
import { Controller, useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { adminClient } from "../../admin-client";
@ -27,6 +30,7 @@ import { useServerInfo } from "../../context/server-info/ServerInfoProvider";
import { useWhoAmI } from "../../context/whoami/WhoAmI";
import { DEFAULT_LOCALE } from "../../i18n/i18n";
import { localeToDisplayName } from "../../util";
import useLocaleSort, { mapByKey } from "../../utils/useLocaleSort";
type EffectiveMessageBundlesProps = {
defaultSupportedLocales: string[];
@ -53,6 +57,7 @@ export const EffectiveMessageBundles = ({
const { realm } = useRealm();
const serverInfo = useServerInfo();
const { whoAmI } = useWhoAmI();
const localeSort = useLocaleSort();
const [searchDropdownOpen, setSearchDropdownOpen] = useState(false);
const [searchPerformed, setSearchPerformed] = useState(false);
const [selectThemesOpen, setSelectThemesOpen] = useState(false);
@ -61,15 +66,29 @@ export const EffectiveMessageBundles = ({
const [activeFilters, setActiveFilters] = useState<
Partial<EffectiveMessageBundlesSearchForm>
>({});
const themes = serverInfo.themes;
const themeKeys = themes
? Object.keys(themes).sort((a, b) => a.localeCompare(b))
: [];
const themeTypes = Object.values(themes!)
.flatMap((theme) => theme.map((item) => item.name))
.filter((value, index, self) => self.indexOf(value) === index)
.sort((a, b) => a.localeCompare(b));
const [key, setKey] = useState(0);
const themes = serverInfo.themes;
const themeTypes = useMemo(() => {
if (!themes) {
return [];
}
return localeSort(Object.keys(themes), (key) => key);
}, [themes]);
const themeNames = useMemo(() => {
if (!themes) {
return [];
}
return localeSort(
Object.values(themes)
.flatMap((theme) => theme.map((item) => item.name))
.filter((value, index, self) => self.indexOf(value) === index),
(name) => name,
);
}, [themes]);
const filterLabels: Record<keyof EffectiveMessageBundlesSearchForm, string> =
{
@ -82,7 +101,7 @@ export const EffectiveMessageBundles = ({
const {
getValues,
reset,
formState: { isDirty },
formState: { isDirty, isValid },
control,
} = useForm<EffectiveMessageBundlesSearchForm>({
mode: "onChange",
@ -101,11 +120,18 @@ export const EffectiveMessageBundles = ({
},
);
return filter.hasWords.length > 0
? messages.filter((m) =>
filter.hasWords.some((f) => m.value.includes(f)),
)
: messages;
const filteredMessages =
filter.hasWords.length > 0
? messages.filter((m) =>
filter.hasWords.some(
(f) => m.value.includes(f) || m.key.includes(f),
),
)
: messages;
const sortedMessages = localeSort([...filteredMessages], mapByKey("key"));
return sortedMessages;
} catch (error) {
return [];
}
@ -159,6 +185,13 @@ export const EffectiveMessageBundles = ({
direction={{ default: "column" }}
spaceItems={{ default: "spaceItemsNone" }}
>
<FlexItem>
<TextContent>
<Text className="pf-u-mb-md pf-u-mt-0" component={TextVariants.p}>
{t("effectiveMessageBundlesDescription")}
</Text>
</TextContent>
</FlexItem>
<FlexItem>
<Dropdown
id="effective-message-bundles-search-select"
@ -175,13 +208,16 @@ export const EffectiveMessageBundles = ({
>
<Form
isHorizontal
className="pf-c-form pf-u-mx-lg pf-u-mb-lg"
className="pf-c-form pf-u-mx-lg pf-u-mb-lg pf-u-w-25vw"
data-testid="effectiveMessageBundlesSearchForm"
>
<FormGroup label={t("theme")} fieldId="kc-theme">
<FormGroup label={t("theme")} fieldId="kc-theme" isRequired>
<Controller
name="theme"
control={control}
rules={{
validate: (value) => (value || "").length > 0,
}}
render={({ field }) => (
<Select
name="theme"
@ -227,7 +263,7 @@ export const EffectiveMessageBundles = ({
isDisabled
/>,
].concat(
themeKeys.map((option) => (
themeNames.map((option) => (
<SelectOption key={option} value={option} />
)),
)}
@ -235,10 +271,17 @@ export const EffectiveMessageBundles = ({
)}
/>
</FormGroup>
<FormGroup label={t("themeType")} fieldId="kc-themeType">
<FormGroup
label={t("themeType")}
fieldId="kc-themeType"
isRequired
>
<Controller
name="themeType"
control={control}
rules={{
validate: (value) => (value || "").length > 0,
}}
render={({ field }) => (
<Select
name="themeType"
@ -292,10 +335,13 @@ export const EffectiveMessageBundles = ({
)}
/>
</FormGroup>
<FormGroup label={t("language")} fieldId="kc-language">
<FormGroup label={t("language")} fieldId="kc-language" isRequired>
<Controller
name="locale"
control={control}
rules={{
validate: (value) => (value || "").length > 0,
}}
render={({ field }) => (
<Select
name="language"
@ -402,7 +448,7 @@ export const EffectiveMessageBundles = ({
submitSearch();
}}
data-testid="search-effective-message-bundles-btn"
isDisabled={!isDirty}
isDisabled={!isValid}
>
{t("search")}
</Button>
@ -417,6 +463,14 @@ export const EffectiveMessageBundles = ({
</ActionGroup>
</Form>
</Dropdown>
<Button
variant="primary"
className="pf-u-ml-md"
onClick={() => submitSearch()}
data-testid="refresh-effective-message-bundles-btn"
>
{t("refresh")}
</Button>
</FlexItem>
<FlexItem>
{Object.entries(activeFilters).length > 0 && (

View file

@ -29,8 +29,8 @@ export class ServerInfo extends Resource {
EffectiveMessageBundleRepresentation[]
>({
method: "GET",
path: "/resources/{realm}/{theme}/{locale}",
urlParamKeys: ["realm", "theme", "locale"],
queryParamKeys: ["themeType", "source"],
path: "/resources/{realm}/{themeType}/{locale}",
urlParamKeys: ["realm", "themeType", "locale"],
queryParamKeys: ["theme", "source"],
});
}