import { ActionGroup, Button, Chip, ChipGroup, Divider, Flex, FlexItem, Form, FormGroup, Select, SelectOption, SelectVariant, Text, TextContent, TextVariants, } from "@patternfly/react-core"; import { pickBy } from "lodash-es"; import { useMemo, useState } from "react"; import { Controller, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { adminClient } from "../../admin-client"; import { KeycloakTextInput } from "../../components/keycloak-text-input/KeycloakTextInput"; import { ListEmptyState } from "../../components/list-empty-state/ListEmptyState"; import { KeycloakDataTable } from "../../components/table-toolbar/KeycloakDataTable"; import { useRealm } from "../../context/realm-context/RealmContext"; 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"; import DropdownPanel from "../../components/dropdown-panel/DropdownPanel"; type EffectiveMessageBundlesProps = { defaultSupportedLocales: string[]; defaultLocales: string[]; }; type EffectiveMessageBundlesSearchForm = { theme: string; themeType: string; locale: string; hasWords: string[]; }; const defaultValues: EffectiveMessageBundlesSearchForm = { theme: "", themeType: "", locale: "", hasWords: [], }; export const EffectiveMessageBundles = ({ defaultSupportedLocales, defaultLocales, }: EffectiveMessageBundlesProps) => { const { t } = useTranslation(); 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); const [selectThemeTypeOpen, setSelectThemeTypeOpen] = useState(false); const [selectLanguageOpen, setSelectLanguageOpen] = useState(false); const [activeFilters, setActiveFilters] = useState< Partial >({}); 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 as Record) .flatMap((theme) => theme.map((item) => item.name)) .filter((value, index, self) => self.indexOf(value) === index), (name) => name, ); }, [themes]); const combinedLocales = useMemo(() => { return Array.from(new Set([...defaultLocales, ...defaultSupportedLocales])); }, [defaultLocales, defaultSupportedLocales]); const filterLabels: Record = { theme: t("theme"), themeType: t("themeType"), locale: t("language"), hasWords: t("hasWords"), }; const { getValues, reset, formState: { isDirty, isValid }, control, } = useForm({ mode: "onChange", defaultValues, }); const loader = async () => { try { const filter = getValues(); const requiredKeys = ["theme", "themeType", "locale"]; const shouldReturnEmpty = requiredKeys.some( (key) => !filter[key as keyof EffectiveMessageBundlesSearchForm], ); if (shouldReturnEmpty) { return []; } const messages = await adminClient.serverInfo.findEffectiveMessageBundles( { realm, ...filter, locale: filter.locale || DEFAULT_LOCALE, source: true, }, ); 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 []; } }; function submitSearch() { setSearchDropdownOpen(false); commitFilters(); } function resetSearch() { reset(); commitFilters(); } function removeFilter(key: keyof EffectiveMessageBundlesSearchForm) { const formValues: EffectiveMessageBundlesSearchForm = { ...getValues() }; delete formValues[key]; reset({ ...defaultValues, ...formValues }); commitFilters(); } function removeFilterValue( key: keyof EffectiveMessageBundlesSearchForm, valueToRemove: string, ) { const formValues = getValues(); const fieldValue = formValues[key]; const newFieldValue = Array.isArray(fieldValue) ? fieldValue.filter((val) => val !== valueToRemove) : fieldValue; reset({ ...formValues, [key]: newFieldValue }); commitFilters(); } function commitFilters() { const newFilters: Partial = pickBy( getValues(), (value) => value !== "" || (Array.isArray(value) && value.length > 0), ); setActiveFilters(newFilters); setKey(key + 1); } const effectiveMessageBunldesSearchFormDisplay = () => { return ( {t("effectiveMessageBundlesDescription")}
e.preventDefault()} > (value || "").length > 0, }} render={({ field }) => ( )} /> (value || "").length > 0, }} render={({ field }) => ( )} /> (value || "").length > 0, }} render={({ field }) => ( )} /> (
{ const target = e.target as HTMLInputElement; const input = target.value; if (input.trim().length === 0) { field.onChange([]); } else { const words = input .split(" ") .map((word) => word.trim()); field.onChange(words); } }} /> {field.value.map((word, index) => ( { e.stopPropagation(); const newWords = field.value.filter( (_, i) => i !== index, ); field.onChange(newWords); }} > {word} ))}
)} />
{Object.entries(activeFilters).length > 0 && ( <> {Object.entries(activeFilters).map((filter) => { const [key, value] = filter as [ keyof EffectiveMessageBundlesSearchForm, string | string[], ]; return ( removeFilter(key)} > {typeof value === "string" ? ( {key === "locale" ? localeToDisplayName( value, whoAmI.getLocale(), )?.toLowerCase() : value} ) : ( value.map((entry) => ( removeFilterValue(key, entry)} > {entry} )) )} ); })} )}
); }; if (!searchPerformed) { return ( <>
{effectiveMessageBunldesSearchFormDisplay()}
); } return ( } isSearching={Object.keys(activeFilters).length > 0} /> ); };