dashpresshq/dashpress

View on GitHub
src/frontend/components/app/form/input/select-async.tsx

Summary

Maintainability
B
5 hrs
Test Coverage
import { useLingui } from "@lingui/react";
import { useMemo, useState } from "react";
import { useDebounce } from "react-use";

import { ErrorAlert } from "@/components/app/alert";
import { useApi } from "@/frontend/lib/data/useApi";
import type { ILabelValue } from "@/shared/types/options";
import { transformLabelValueToSelectData } from "@/translations/fake";

import { FormSelect } from "./select";
import type { IBaseFormSelect } from "./types";

const limit = 50;

interface IProps extends IBaseFormSelect {
  url: string;
  referenceUrl?: (value: string) => string;
}

export function AsyncFormSelect(props: IProps) {
  const { _ } = useLingui();

  const { input, url, referenceUrl } = props;

  const [search, setSearch] = useState("");
  const [debounceSearch, setDebounceSearch] = useState("");

  const fullData = useApi<ILabelValue[]>(url, {
    defaultData: [],
  });

  const selectOptions = useApi<ILabelValue[]>(
    debounceSearch ? `${url}?search=${debounceSearch}` : url,
    {
      defaultData: [],
    }
  );

  const currentLabelFromSelection = useMemo(() => {
    const isValueInFirstDataLoad = fullData.data.find(
      ({ value }: ILabelValue) => String(value) === String(input.value)
    );

    if (isValueInFirstDataLoad) {
      return _(isValueInFirstDataLoad.label);
    }

    const isValueInSelectionOptions = selectOptions.data.find(
      ({ value }: ILabelValue) => String(value) === String(input.value)
    );

    if (isValueInSelectionOptions) {
      return _(isValueInSelectionOptions.label);
    }

    return undefined;
  }, [fullData, selectOptions, input.value, _]);

  const referenceLabel = useApi(referenceUrl?.(input.value), {
    defaultData: "",
    enabled:
      !!referenceUrl &&
      !!input.value &&
      !(currentLabelFromSelection || fullData.isLoading),
  });

  useDebounce(
    () => {
      setDebounceSearch(search);
    },
    700,
    [search]
  );

  if (fullData.error || selectOptions.error) {
    return <ErrorAlert message={fullData.error || selectOptions.error} />;
  }

  const isLoading = selectOptions.isLoading || fullData.isLoading;

  if (fullData.data.length >= limit) {
    return (
      <FormSelect
        {...props}
        selectData={transformLabelValueToSelectData(selectOptions.data)}
        isLoading={isLoading}
        onSearch={{
          isLoading: selectOptions.isLoading,
          onChange: setSearch,
          value: search,
          valueLabel: currentLabelFromSelection || referenceLabel.data,
        }}
      />
    );
  }

  return (
    <FormSelect
      {...props}
      selectData={transformLabelValueToSelectData(fullData.data)}
      isLoading={isLoading}
    />
  );
}