import React, { useState, useEffect } from "react"; import { Autocomplete, TextField, Checkbox, CircularProgress } from "@mui/material"; import { useField } from "formik"; import { DEBOUNCE_WAIT_250 } from "../../../utils/constants"; // Stays local: uicore's async-select.js lacks defaultOptions, filterOptions, // and the plain-value-sync effect this component has. company-form.js // depends on defaultOptions specifically. Deferred as a follow-up rather // than migrated blindly in this pass. const MuiFormikAsyncAutocomplete = ({ name, queryFunction, multiple = false, placeholder = "Select...", plainValue = false, hiddenOptions = [], formatOption = (item) => ({ value: item.id.toString(), label: item.name }), formatSelectedValue = null, queryParams = [], isMulti = false, disabled = false, defaultOptions = false, ...rest }) => { const [field, meta, helpers] = useField(name); const [options, setOptions] = useState([]); const [loading, setLoading] = useState(false); const [searchTerm, setSearchTerm] = useState(""); const isMultiSelect = isMulti || multiple; const value = field.value || (isMultiSelect ? [] : null); // Sync a plain stored value back to the full option object useEffect(() => { if (!field.value || typeof field.value === "object" || options.length === 0) return; const match = options.find((o) => o.value === String(field.value)); if (match) helpers.setValue(match); }, [options, field.value]); const error = meta.touched && meta.error; const fetchOptions = async (input = "") => { setLoading(true); try { await queryFunction(input, ...queryParams, (results) => { const normalized = results .filter((r) => !hiddenOptions.includes(r.id)) .map(formatOption); setOptions(normalized); setLoading(false); }); } catch (err) { console.error("Error fetching options:", err); setLoading(false); } }; useEffect(() => { if (!defaultOptions && searchTerm) { const delayDebounce = setTimeout(() => { fetchOptions(searchTerm); }, DEBOUNCE_WAIT_250); return () => clearTimeout(delayDebounce); } }, [searchTerm]); // preload empty useEffect(() => { fetchOptions(""); }, []); const handleChange = (event, selected) => { if (!isMultiSelect) { const selectedValue = plainValue ? selected?.value || "" : selected; helpers.setValue(selectedValue); return; } const selectedItems = plainValue ? selected.map((s) => s.value) : selected.map((s) => formatSelectedValue ? formatSelectedValue(s) : { id: parseInt(s.value), name: s.label } ); helpers.setValue(selectedItems); }; return ( option.label || ""} isOptionEqualToValue={(option, value) => option.value === value.value} onInputChange={ !defaultOptions ? (e, newInput) => setSearchTerm(newInput) : undefined } filterOptions={ defaultOptions ? (options, { inputValue }) => options.filter((opt) => opt.label.toLowerCase().includes(inputValue.toLowerCase()) ) : undefined // no client-side filter — let the API handle it via searchTerm } renderInput={(params) => ( {loading && } {params.InputProps?.endAdornment} ) }, inputLabel: { shrink: false } }} sx={{ "& input::placeholder": { color: "#00000061", opacity: 1 } }} /> )} renderOption={(props, option, { selected }) => (
  • {isMultiSelect && } {option.label}
  • )} /> ); }; export default MuiFormikAsyncAutocomplete;