UNPKG

@keycloakify/keycloak-admin-ui

Version:
208 lines 15.8 kB
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import { KeycloakSelect, SelectVariant } from "../../ui-shared"; import { ActionGroup, Button, Chip, ChipGroup, Divider, Flex, FlexItem, Form, FormGroup, SelectOption, Text, TextContent, TextInput, 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 { useAdminClient } from "../../admin-client"; import DropdownPanel from "../../components/dropdown-panel/DropdownPanel"; import { ListEmptyState } from "../../ui-shared"; import { KeycloakDataTable } from "../../ui-shared"; 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"; const defaultValues = { theme: "", themeType: "", locale: "", hasWords: [], }; export const EffectiveMessageBundles = ({ defaultSupportedLocales, defaultLocales, }) => { const { adminClient } = useAdminClient(); 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({}); 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 combinedLocales = useMemo(() => { return Array.from(new Set([...defaultLocales, ...defaultSupportedLocales])); }, [defaultLocales, defaultSupportedLocales]); const filterLabels = { 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]); 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 (_a) { return []; } }; function submitSearch() { setSearchDropdownOpen(false); commitFilters(); } function resetSearch() { reset(); commitFilters(); } function removeFilter(key) { const formValues = { ...getValues() }; delete formValues[key]; reset({ ...defaultValues, ...formValues }); commitFilters(); } function removeFilterValue(key, valueToRemove) { 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 = pickBy(getValues(), (value) => value !== "" || (Array.isArray(value) && value.length > 0)); setActiveFilters(newFilters); setKey(key + 1); } const effectiveMessageBunldesSearchFormDisplay = () => { return (_jsxs(Flex, { direction: { default: "column" }, spaceItems: { default: "spaceItemsNone" }, children: [_jsx(FlexItem, { children: _jsx(TextContent, { children: _jsx(Text, { className: "pf-v5-u-mb-md pf-v5-u-mt-0 pf-v5-u-mr-md", component: TextVariants.p, children: t("effectiveMessageBundlesDescription") }) }) }), _jsx(FlexItem, { children: _jsx(DropdownPanel, { buttonText: t("searchForEffectiveMessageBundles"), setSearchDropdownOpen: setSearchDropdownOpen, searchDropdownOpen: searchDropdownOpen, marginRight: "2.5rem", width: "15vw", children: _jsxs(Form, { isHorizontal: true, className: "pf-v5-u-w-25vw", "data-testid": "effectiveMessageBundlesSearchForm", onSubmit: (e) => e.preventDefault(), children: [_jsx(FormGroup, { label: t("theme"), fieldId: "kc-theme", isRequired: true, children: _jsx(Controller, { name: "theme", control: control, defaultValue: "", rules: { validate: (value) => (value || "").length > 0, }, render: ({ field }) => (_jsx(KeycloakSelect, { "data-testid": "effective_message_bundles-theme-searchField", chipGroupProps: { numChips: 1, expandedText: t("hide"), collapsedText: t("showRemaining"), }, variant: SelectVariant.single, typeAheadAriaLabel: "Select", onToggle: (val) => setSelectThemesOpen(val), selections: field.value, onSelect: (selectedValue) => { field.onChange(selectedValue.toString()); setSelectThemesOpen(false); }, onClear: () => { field.onChange(""); }, isOpen: selectThemesOpen, "aria-label": t("selectTheme"), chipGroupComponent: _jsx(ChipGroup, { children: _jsx(Chip, { onClick: (theme) => { theme.stopPropagation(); field.onChange(""); }, children: field.value }, field.value) }), children: [ _jsx(SelectOption, { label: t("selectTheme"), isDisabled: true, children: t("selectTheme") }, "theme_placeholder"), ].concat(themeNames.map((option) => (_jsx(SelectOption, { value: option, children: option }, option)))) })) }) }), _jsx(FormGroup, { label: t("themeType"), fieldId: "kc-themeType", isRequired: true, children: _jsx(Controller, { name: "themeType", control: control, defaultValue: "", rules: { validate: (value) => (value || "").length > 0, }, render: ({ field }) => (_jsx(KeycloakSelect, { "data-testid": "effective-message-bundles-feature-searchField", chipGroupProps: { numChips: 1, expandedText: t("hide"), collapsedText: t("showRemaining"), }, variant: SelectVariant.single, typeAheadAriaLabel: "Select", onToggle: (val) => setSelectThemeTypeOpen(val), selections: field.value, onSelect: (selectedValue) => { field.onChange(selectedValue.toString()); setSelectThemeTypeOpen(false); }, onClear: () => { field.onChange(""); }, isOpen: selectThemeTypeOpen, "aria-label": t("selectThemeType"), chipGroupComponent: _jsx(ChipGroup, { children: _jsx(Chip, { onClick: (themeType) => { themeType.stopPropagation(); field.onChange(""); }, children: field.value }, field.value) }), children: [ _jsx(SelectOption, { label: t("selectThemeType"), isDisabled: true, children: t("selectThemeType") }, "themeType_placeholder"), ].concat(themeTypes.map((option) => (_jsx(SelectOption, { value: option, children: option }, option)))) })) }) }), _jsx(FormGroup, { label: t("language"), fieldId: "kc-language", isRequired: true, children: _jsx(Controller, { name: "locale", control: control, defaultValue: "", rules: { validate: (value) => (value || "").length > 0, }, render: ({ field }) => (_jsx(KeycloakSelect, { "data-testid": "effective-message-bundles-language-searchField", chipGroupProps: { numChips: 1, expandedText: t("hide"), collapsedText: t("showRemaining"), }, variant: SelectVariant.single, typeAheadAriaLabel: "Select", onToggle: (val) => setSelectLanguageOpen(val), selections: field.value, onSelect: (selectedValue) => { field.onChange(selectedValue.toString()); setSelectLanguageOpen(false); }, onClear: () => { field.onChange(""); }, isOpen: selectLanguageOpen, "aria-label": t("selectLanguage"), chipGroupComponent: _jsx(ChipGroup, { children: field.value ? (_jsx(Chip, { onClick: (language) => { language.stopPropagation(); field.onChange(""); }, children: localeToDisplayName(field.value, whoAmI.getLocale()) }, field.value)) : null }), children: [ _jsx(SelectOption, { label: t("selectLanguage"), isDisabled: true, children: t("selectLanguage") }, "language_placeholder"), ].concat(combinedLocales.map((option) => (_jsx(SelectOption, { value: option, children: localeToDisplayName(option, whoAmI.getLocale()) }, option)))) })) }) }), _jsx(FormGroup, { label: t("hasWords"), fieldId: "kc-hasWords", children: _jsx(Controller, { name: "hasWords", control: control, render: ({ field }) => (_jsxs("div", { children: [_jsx(TextInput, { id: "kc-hasWords", "data-testid": "effective-message-bundles-hasWords-searchField", value: field.value.join(" "), onChange: (e) => { const target = e.target; const input = target.value; if (input.trim().length === 0) { field.onChange([]); } else { const words = input .split(" ") .map((word) => word.trim()); field.onChange(words); } } }), _jsx(ChipGroup, { children: field.value.map((word, index) => (_jsx(Chip, { onClick: (e) => { e.stopPropagation(); const newWords = field.value.filter((_, i) => i !== index); field.onChange(newWords); }, children: word }, index))) })] })) }) }), _jsxs(ActionGroup, { className: "pf-v5-u-mt-sm", children: [_jsx(Button, { variant: "primary", onClick: () => { setSearchPerformed(true); submitSearch(); }, "data-testid": "search-effective-message-bundles-btn", isDisabled: !isValid, children: t("search") }), _jsx(Button, { variant: "secondary", onClick: resetSearch, "data-testid": "reset-search-effective-message-bundles-btn", isDisabled: !isDirty, children: t("reset") })] })] }) }) }), _jsx(FlexItem, { children: Object.entries(activeFilters).length > 0 && (_jsx(_Fragment, { children: Object.entries(activeFilters).map((filter) => { var _a; const [key, value] = filter; return (_jsx(ChipGroup, { className: "pf-v5-u-mt-md pf-v5-u-mr-md", categoryName: filterLabels[key], isClosable: true, onClick: () => removeFilter(key), children: typeof value === "string" ? (_jsx(Chip, { isReadOnly: true, children: key === "locale" ? (_a = localeToDisplayName(value, whoAmI.getLocale())) === null || _a === void 0 ? void 0 : _a.toLowerCase() : value })) : (value.map((entry) => (_jsx(Chip, { onClick: () => removeFilterValue(key, entry), children: entry }, entry)))) }, key)); }) })) })] })); }; if (!searchPerformed) { return (_jsxs(_Fragment, { children: [_jsx("div", { className: "pf-v5-u-py-lg pf-v5-u-pl-md", children: effectiveMessageBunldesSearchFormDisplay() }), _jsx(Divider, {}), _jsx(ListEmptyState, { message: t("emptyEffectiveMessageBundles"), instructions: t("emptyEffectiveMessageBundlesInstructions"), isSearchVariant: true })] })); } return (_jsx(KeycloakDataTable, { loader: loader, ariaLabelKey: "effectiveMessageBundles", toolbarItem: effectiveMessageBunldesSearchFormDisplay(), columns: [ { name: "key", displayKey: "key", }, { name: "value", displayKey: "value", }, ], emptyState: _jsx(ListEmptyState, { message: t("noSearchResults"), instructions: t("noSearchResultsInstructions") }), isSearching: Object.keys(activeFilters).length > 0 }, key)); }; //# sourceMappingURL=EffectiveMessageBundles.js.map