Source: src/js/models/searchSelect/AccountSearchSelect.js

"use strict";

define(["models/searchSelect/SearchSelect", "models/LookupModel"], (
  SearchSelect,
  LookupModel,
) => {
  /**
   * @class AccountSearchSelect
   * @classdesc An extension of SearchSelect that sets the options to the query
   * fields (e.g. Solr fields) available for searching.
   * @classcategory Models/SearchSelect
   * @since 2.31.0
   * @augments Backbone.Model
   */
  const AccountSearchSelect = SearchSelect.extend({
    /** @lends AccountSearchSelect.prototype */

    /** @inheritdoc */
    defaults() {
      return {
        ...SearchSelect.prototype.defaults(),
        placeholderText: "Start typing a name",
        inputLabel: "Search for a person or group",
        allowMulti: true,
        allowAdditions: false,
        apiSettings: {
          responseAsync: this.responseAsync.bind(this),
        },
      };
    },

    /** @inheritdoc */
    initialize() {
      if (!MetacatUI.appLookupModel)
        MetacatUI.appLookupModel = new LookupModel();
      SearchSelect.prototype.initialize.call(this);
    },

    /**
     * Handles the async response for the Accounts lookup
     * @param {object} settings - The settings object passed by Formantic-UI
     * @param {Function} callback - The callback function passed by Formantic-UI
     */
    responseAsync(settings, callback) {
      const model = this;

      // The search term that the user has typed into the input
      const searchTerm = settings.urlData.query;
      // To return, fail unless we have results
      const results = { success: false };

      // Only use the account lookup service is the user has typed at least two
      // characters. Otherwise, the callback function is never called.
      if (searchTerm.length < 2) callback(results);

      model
        .getAccountDetails(searchTerm)
        .then((response) => {
          if (response && response.length) {
            results.results = model.formatResults(response, false);
            results.success = true;
          }
          callback(results);
        })
        .catch(() => {
          callback(results);
        });
    },

    /**
     * Formats the results from the account lookup service for the dropdown
     * @param {object[]} results - The results from the account lookup service
     * @param {boolean} forTemplate - Whether to format the results for the
     * template in the SearchSelect view or directly for Formantic-UI
     * @returns {object[]} - The formatted results
     */
    formatResults(results, forTemplate = false) {
      return results.map((result) => this.formatResult(result, forTemplate));
    },

    /**
     * Formats a single result from the account lookup service
     * @param {object} rawResult - The result from the account lookup service
     * @param {boolean} forTemplate - See formatResults
     * @returns {object} - The formatted result
     */
    formatResult(rawResult, forTemplate = false) {
      // clone the result
      const result = { ...rawResult };

      let icon = "";
      if (result.type === "person") {
        icon = "user";
      } else if (result.type === "group") {
        icon = "group";
      }

      // Get the ID which is saved in the parentheses of the label
      const idRegex = /\(([^)]+)\)$/;
      const match = result.label.match(idRegex);
      const id = match?.[1] || "";

      // The label will be the name without the ID
      const accountName = result.label.replace(idRegex, "").trim();

      // Result for the template in the SearchSelect view
      if (forTemplate) {
        return {
          ...result,
          description: `Account ID: ${id}`,
          label: accountName,
          icon,
        };
      }
      // Result for a Formantic-UI item
      const formatIcon = icon
        ? `<i class="icon icon-on-left icon-${icon}"></i>`
        : "";
      return {
        name: `${formatIcon} ${accountName}`,
        value: result.value,
        description: id,
        descriptionVertical: true,
        text: `${formatIcon} ${accountName}`,
      };
    },

    /**
     * Promisify the getAccountsAutocomplete function from the LookupModel
     * @param {string} searchTerm - The account ID, name, or partial name to search for
     * @returns {Promise<object[]>} - A promise that resolves with the results
     */
    async getAccountDetails(searchTerm) {
      return new Promise((resolve, reject) => {
        MetacatUI.appLookupModel.getAccountsAutocomplete(
          { term: searchTerm },
          (results) => {
            if (results) {
              resolve(results);
            } else {
              reject(new Error("Failed to fetch account details"));
            }
          },
        );
      });
    },

    /**
     * Use the account lookup service to match the pre-selected values to the
     * account holder's name to use as a label.
     */
    async setOptionsForPreselected() {
      const selected = this.get("selected");
      if (!selected?.length) return;
      const results = await Promise.all(
        selected.map((accountId) => this.getAccountDetails(accountId)),
      );
      const formattedResults = this.formatResults(results.flat(), true);
      this.updateOptions(formattedResults);
    },
  });

  return AccountSearchSelect;
});