/* eslint-disable */ // @ts-nocheck import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation"; import { KeycloakSelect, ListEmptyState, PaginatingTableToolbar, SelectVariant, useAlerts, } from "../../../shared/keycloak-ui-shared"; import { AlertVariant, Button, ButtonVariant, Divider, Dropdown, DropdownItem, DropdownList, Form, FormGroup, MenuToggle, SelectGroup, SelectOption, Text, TextContent, TextInput, TextVariants, ToolbarItem, } from "../../../shared/@patternfly/react-core"; import { CheckIcon, EllipsisVIcon, PencilAltIcon, SearchIcon, TimesIcon, } from "../../../shared/@patternfly/react-icons"; import { ActionsColumn, IRow, IRowCell, Table, Tbody, Td, Th, Thead, Tr, } from "../../../shared/@patternfly/react-table"; import { cloneDeep, isEqual, uniqWith } from "lodash-es"; import { ChangeEvent, useEffect, useState, type FormEvent } from "react"; import { useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { useAdminClient } from "../../admin-client"; import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog"; import { KeyValueType } from "../../components/key-value-form/key-value-convert"; import { useRealm } from "../../context/realm-context/RealmContext"; import { useWhoAmI } from "../../context/whoami/WhoAmI"; import { DEFAULT_LOCALE } from "../../i18n/constants"; import { localeToDisplayName } from "../../util"; import { AddTranslationModal } from "../AddTranslationModal"; type RealmOverridesProps = { internationalizationEnabled: boolean; watchSupportedLocales: string[]; realm: RealmRepresentation; tableData: Record[] | undefined; }; type EditStatesType = { [key: number]: boolean }; export type TranslationForm = { key: string; value: string; translation: KeyValueType; }; export enum RowEditAction { Save = "save", Cancel = "cancel", Edit = "edit", Delete = "delete", } export const RealmOverrides = ({ internationalizationEnabled, watchSupportedLocales, realm, tableData, }: RealmOverridesProps) => { const { adminClient } = useAdminClient(); const { t, i18n } = useTranslation(); const [addTranslationModalOpen, setAddTranslationModalOpen] = useState(false); const [filterDropdownOpen, setFilterDropdownOpen] = useState(false); const [translations, setTranslations] = useState<[string, string][]>([]); const [selectMenuLocale, setSelectMenuLocale] = useState(DEFAULT_LOCALE); const [kebabOpen, setKebabOpen] = useState(false); const { getValues, handleSubmit } = useForm(); const [selectMenuValueSelected, setSelectMenuValueSelected] = useState(false); const [tableRows, setTableRows] = useState([]); const [tableKey, setTableKey] = useState(0); const [max, setMax] = useState(10); const [first, setFirst] = useState(0); const [filter, setFilter] = useState(""); const translationForm = useForm({ mode: "onChange" }); const { addAlert, addError } = useAlerts(); const { realm: currentRealm } = useRealm(); const { whoAmI } = useWhoAmI(); const [selectedRowKeys, setSelectedRowKeys] = useState([]); const [areAllRowsSelected, setAreAllRowsSelected] = useState(false); const [editStates, setEditStates] = useState({}); const [formValue, setFormValue] = useState(""); const refreshTable = () => { setTableKey(tableKey + 1); }; useEffect(() => { const fetchLocalizationTexts = async () => { try { let result = await adminClient.realms.getRealmLocalizationTexts({ first, max, realm: realm.realm!, selectedLocale: selectMenuLocale || getValues("defaultLocale") || whoAmI.locale, }); setTranslations(Object.entries(result)); if (filter) { const searchInTranslations = (idx: number) => { return Object.entries(result).filter((i) => i[idx].includes(filter), ); }; const filtered = uniqWith( searchInTranslations(0).concat(searchInTranslations(1)), isEqual, ); result = Object.fromEntries(filtered); } return Object.entries(result).slice(first, first + max); } catch { return []; } }; void fetchLocalizationTexts().then((translations) => { const updatedRows: IRow[] = translations.map( (translation): IRow => ({ rowEditBtnAriaLabel: () => t("rowEditBtnAriaLabel", { translation: translation[1], }), rowSaveBtnAriaLabel: () => t("rowSaveBtnAriaLabel", { translation: translation[1], }), rowCancelBtnAriaLabel: () => t("rowCancelBtnAriaLabel", { translation: translation[1], }), cells: [ { title: translation[0], props: { value: translation[0], }, }, { title: translation[1], props: { value: translation[1], }, }, ], }), ); setTableRows(updatedRows); }); }, [tableKey, tableData, first, max, filter]); const handleModalToggle = () => { setAddTranslationModalOpen(!addTranslationModalOpen); }; const options = [ {localeToDisplayName(DEFAULT_LOCALE, whoAmI.displayName)} , , {watchSupportedLocales.map((locale) => ( {localeToDisplayName(locale, whoAmI.locale)} ))} , ]; const addKeyValue = async (pair: KeyValueType): Promise => { try { await adminClient.realms.addLocalization( { realm: currentRealm!, selectedLocale: selectMenuLocale || getValues("defaultLocale") || DEFAULT_LOCALE, key: pair.key, }, pair.value, ); adminClient.setConfig({ realmName: currentRealm!, }); refreshTable(); translationForm.setValue("key", ""); translationForm.setValue("value", ""); await i18n.reloadResources(); addAlert(t("addTranslationSuccess"), AlertVariant.success); } catch (error) { addError("addTranslationError", error); } }; const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({ titleKey: "deleteConfirmTranslationTitle", messageKey: t("translationDeleteConfirmDialog", { count: selectedRowKeys.length, }), continueButtonLabel: "delete", continueButtonVariant: ButtonVariant.danger, onCancel: () => { setSelectedRowKeys([]); setAreAllRowsSelected(false); }, onConfirm: async () => { try { for (const key of selectedRowKeys) { delete ( i18n.store.data[whoAmI.locale][currentRealm] as Record< string, string > )[key]; await adminClient.realms.deleteRealmLocalizationTexts({ realm: currentRealm!, selectedLocale: selectMenuLocale, key: key, }); } setAreAllRowsSelected(false); setSelectedRowKeys([]); refreshTable(); addAlert(t("deleteAllTranslationsSuccess"), AlertVariant.success); } catch (error) { addError("deleteAllTranslationsError", error); } }, }); const handleRowSelect = ( event: ChangeEvent, rowIndex: number, ) => { const selectedKey = (tableRows[rowIndex].cells?.[0] as IRowCell).props .value; if (event.target.checked) { setSelectedRowKeys((prevSelected) => [...prevSelected, selectedKey]); } else { setSelectedRowKeys((prevSelected) => prevSelected.filter((key) => key !== selectedKey), ); } setAreAllRowsSelected( tableRows.length === selectedRowKeys.length + (event.target.checked ? 1 : -1), ); }; const toggleSelectAllRows = () => { if (areAllRowsSelected) { setSelectedRowKeys([]); } else { setSelectedRowKeys( tableRows.map((row) => (row.cells?.[0] as IRowCell).props.value), ); } setAreAllRowsSelected(!areAllRowsSelected); }; const isRowSelected = (key: any) => { return selectedRowKeys.includes(key); }; const onSubmit = async (inputValue: string, rowIndex: number) => { const newRows = cloneDeep(tableRows); const newRow = cloneDeep(newRows[rowIndex]); (newRow.cells?.[1] as IRowCell).props.value = inputValue; newRows[rowIndex] = newRow; try { const key = (newRow.cells?.[0] as IRowCell).props.value; const value = (newRow.cells?.[1] as IRowCell).props.value; await adminClient.realms.addLocalization( { realm: realm.realm!, selectedLocale: selectMenuLocale || getValues("defaultLocale") || DEFAULT_LOCALE, key, }, value, ); addAlert(t("updateTranslationSuccess"), AlertVariant.success); setTableRows(newRows); } catch (error) { addError("updateTranslationError", error); } setEditStates((prevEditStates) => ({ ...prevEditStates, [rowIndex]: false, })); }; return ( <> {addTranslationModalOpen && ( { await addKeyValue(pair); handleModalToggle(); }} form={translationForm} /> )} {t("realmOverridesDescription")} { setFirst(first); setMax(max); }} inputGroupName={"search"} inputGroupOnEnter={(search) => { setFilter(search); setFirst(0); setMax(10); }} inputGroupPlaceholder={t("searchForTranslation")} toolbarItem={ <> setKebabOpen(isOpen)} toggle={(ref) => ( setKebabOpen(!kebabOpen)} variant="plain" isExpanded={kebabOpen} data-testid="toolbar-deleteBtn" aria-label="kebab" > )} isOpen={kebabOpen} isPlain > { toggleDeleteDialog(); setKebabOpen(false); }} > {t("delete")} } searchTypeComponent={ setFilterDropdownOpen(isExpanded)} onSelect={(value) => { setSelectMenuLocale(value.toString()); setSelectMenuValueSelected(true); refreshTable(); setFilterDropdownOpen(false); }} selections={ selectMenuValueSelected ? localeToDisplayName(selectMenuLocale, whoAmI.locale) : realm.defaultLocale !== "" ? localeToDisplayName(DEFAULT_LOCALE, whoAmI.locale) : t("placeholderText") } > {options} } > {translations.length === 0 && !filter && ( )} {translations.length === 0 && filter && ( )} {translations.length !== 0 && ( {tableRows.map((row, rowIndex) => ( ))}
{t("key")} {t("value")}
handleRowSelect( event as ChangeEvent, rowIndex, ), isSelected: isRowSelected( (row.cells?.[0] as IRowCell).props.value, ), }} /> {(row.cells?.[0] as IRowCell).props.value}
{ await onSubmit(formValue, rowIndex); })} > {editStates[rowIndex] ? ( <> , value: string, ) => { setFormValue(value); }} key={`edit-input-${rowIndex}`} /> )}
{ setSelectedRowKeys([ (row.cells?.[0] as IRowCell).props.value, ]); if (translations.length === 1) { setAreAllRowsSelected(true); } toggleDeleteDialog(); setKebabOpen(false); }, }, ]} />
)}
); };