diff --git a/src/components/shared/DropDown.tsx b/src/components/shared/DropDown.tsx index fa0539104c..83421c81d2 100644 --- a/src/components/shared/DropDown.tsx +++ b/src/components/shared/DropDown.tsx @@ -45,7 +45,7 @@ const DropDown = ({ ref?: React.RefObject> | null> value: T text: string, - options: DropDownOption[], + options?: DropDownOption[], required: boolean, handleChange: (option: {value: T, label: string} | null) => void placeholder: string @@ -66,7 +66,7 @@ const DropDown = ({ optionPaddingTop?: number, optionLineHeight?: string }, - fetchOptions?: () => { label: string, value: string}[] + fetchOptions?: (inputValue: string) => Promise<{ label: string, value: string }[]> }) => { const { t } = useTranslation(); @@ -157,14 +157,29 @@ const DropDown = ({ ) : null; }; + const filterOptions = (inputValue: string) => { + if (options) { + return options.filter(option => + option.label.toLowerCase().includes(inputValue.toLowerCase()), + ); + } + return []; + }; + + const loadOptionsAsync = (inputValue: string, callback: (options: DropDownOption[]) => void) => { + setTimeout(async () => { + callback(formatOptions( + fetchOptions ? await fetchOptions(inputValue) : filterOptions(inputValue), + required, + )); + }, 1000); + }; + const loadOptions = ( inputValue: string, callback: (options: DropDownOption[]) => void, ) => { - callback(formatOptions( - fetchOptions ? fetchOptions() : options, - required, - )); + callback(formatOptions(filterOptions(inputValue), required)); }; @@ -176,10 +191,14 @@ const DropDown = ({ autoFocus: autoFocus, isSearchable: true, value: { value: value, label: text === "" ? placeholder : text }, - options: formatOptions( - options, - required, - ), + defaultOptions: options + ? formatOptions( + options, + required, + ) + : true, + cacheOptions: true, + loadOptions: fetchOptions ? loadOptionsAsync : loadOptions, placeholder: placeholder, onChange: element => handleChange(element as {value: T, label: string}), menuIsOpen: menuIsOpen, @@ -191,31 +210,18 @@ const DropDown = ({ // @ts-expect-error: React-Select typing does not account for the typing of option it itself requires components: { MenuList }, - filterOption: createFilter({ ignoreAccents: false }), // To improve performance on filtering }; return creatable ? ( ) : ( t("SELECT_NO_MATCHING_RESULTS")} - cacheOptions - defaultOptions={formatOptions( - options, - required, - )} - loadOptions={loadOptions} /> ); }; diff --git a/src/components/shared/modals/ResourceDetailsAccessPolicyTab.tsx b/src/components/shared/modals/ResourceDetailsAccessPolicyTab.tsx index 77bc8be567..cdc78708d9 100644 --- a/src/components/shared/modals/ResourceDetailsAccessPolicyTab.tsx +++ b/src/components/shared/modals/ResourceDetailsAccessPolicyTab.tsx @@ -543,11 +543,6 @@ export const AccessPolicyTable = ({ ? formatAclRolesForDropdown(rolesFilteredbyPolicies) : [] } - fetchOptions={() => - roles.length > 0 - ? formatAclRolesForDropdown(rolesFilteredbyPolicies) - : [] - } required={true} creatable={true} handleChange={element => { diff --git a/src/components/shared/wizard/RenderField.tsx b/src/components/shared/wizard/RenderField.tsx index 00b4fbe5c0..b728cd7750 100644 --- a/src/components/shared/wizard/RenderField.tsx +++ b/src/components/shared/wizard/RenderField.tsx @@ -1,8 +1,8 @@ -import React, { useRef, useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import DatePicker from "react-datepicker"; import cn from "classnames"; -import { getMetadataCollectionFieldName } from "../../../utils/resourceUtils"; +import { getMetadataCollectionFieldName, transformListProvider } from "../../../utils/resourceUtils"; import { getCurrentLanguageInformation } from "../../../utils/utils"; import DropDown from "../DropDown"; import { parseISO } from "date-fns"; @@ -10,6 +10,7 @@ import { FieldProps } from "formik"; import { MetadataField } from "../../../slices/eventSlice"; import { GroupBase, SelectInstance } from "react-select"; import TextareaAutosize from "react-textarea-autosize"; +import axios from "axios"; /** * This component renders an editable field for single values depending on the type of the corresponding metadata @@ -65,7 +66,7 @@ const RenderField = ({ )} {metadataField.type === "text" && !!metadataField.collection && - metadataField.collection.length > 0 && ( + ( void ref: React.RefObject>> -}) => { +}) +const EditableSingleSelect = (props: EditableSingleSelectProps) => { const { t } = useTranslation(); + const { + field, + metadataField, + text, + form: { setFieldValue }, + isFirstField, + focused, + setFocused, + ref, + } = props; + + if (metadataField.id === "isPartOf") { + return ; + } + return ( { + const { t } = useTranslation(); + + const [label, setLabel] = useState(""); + + useEffect(() => { + // The metadata catalog only contains the field value, so we need to fetch the label ourselves + const fetchLabelById = async () => { + if (field.value) { + const res = await axios.get<{ [key: string]: string }>(`/admin-ng/resources/SERIES.WRITE_ONLY.json?limit=1&filter=textFilter:${field.value}`); + const data = res.data; + const transformedData = transformListProvider(data); + if (transformedData.length > 0) { + setLabel(transformedData[0].label); + } + } + }; + fetchLabelById(); + }, [field.value]); + + // Fetch collection + const fetchOptions = async (inputValue: string) => { + const res = await axios.get<{ [key: string]: string }>(`/admin-ng/resources/SERIES.WRITE_ONLY.json?filter=textFilter:${inputValue}`); + const data = res.data; + return transformListProvider(data); + }; + + return ( + element && setFieldValue(field.name, element.value)} + placeholder={focused + ? `-- ${t("SELECT_NO_OPTION_SELECTED")} --` + : `${t("SELECT_NO_OPTION_SELECTED")}` + } + customCSS={{ isMetadataStyle: focused ? false : true }} + handleMenuIsOpen={(open: boolean) => setFocused(open)} + openMenuOnFocus + autoFocus={isFirstField} + skipTranslate={!metadataField.translatable} + /> + ); +}; + export default RenderField; diff --git a/src/utils/resourceUtils.ts b/src/utils/resourceUtils.ts index ce72afa7dc..ba6d007bab 100644 --- a/src/utils/resourceUtils.ts +++ b/src/utils/resourceUtils.ts @@ -177,6 +177,27 @@ export const transformMetadataFields = (metadata: MetadataField[]) => { return metadata; }; +export const transformListProvider = (collection: { [key: string]: string }) => { + return Object.entries(collection) + .map(([key, value]) => { + if (isJson(value)) { + // TODO: Handle JSON parsing errors + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const collectionParsed: { [key: string]: string } = JSON.parse(value); + return { + label: collectionParsed.label || value, + value: key, + ...collectionParsed, + }; + } else { + return { + label: value, + value: key, + }; + } + }); +}; + // transform metadata catalog for update via post request export const transformMetadataForUpdate = (catalog: MetadataCatalog, values: { [key: string]: MetadataCatalog["fields"][0]["value"] }) => { const fields: MetadataCatalog["fields"] = [];