Source: listings/bidOnListing.mjs

// Import our custom CSS
// import { get } from 'cypress/types/lodash';
import "../../scss/styles.scss";

import { API_BASE, API_BID_ON_LISTING, API_KEY } from "../settings.mjs";
import { displayListings } from "./index.mjs";
import { load } from "../shared/storage.mjs";
import { ErrorHandler } from "../shared/errorHandler.mjs";

/** @typedef {object} createBidRequest
/* @typed {object}
 * @property {number} amount
 */

/** @typedef  {object} createBidResponse
/* @typed {object}
 * @property {string} id
 * @property {string} title
 * @property {string} description
 * @property {string[]} tags
 * @property {object[]} media
 * @property {string} media.url
 * @property {string} media.alt
 * @property {string} created
 * @property {string} updated
 * @property {string} endsAt
 * @property {object} _count
 * @property {number} _count.bids
 */

// -------------------------------------------------

/**
 * @description Shows or hides a info message.
 * @method statusMsg
 * @param {string} listingId The listing id.
 * @param {boolean} visible If true, shows the msg, otherwise hides it.
 * @param {string} [text] The message to show, or `undefined` if `visible` is false.
 */
export function statusMsg(listingId, visible, text) {
  /** @type {HTMLDivElement} */
  const status = document.querySelector(`article[data-id="${listingId}"] #statusBidMsg`);

  if (visible === true) {
    status.classList.remove("d-none");
    status.classList.add("d-flex");
    status.innerHTML = text;
  } else {
    status.classList.remove("d-flex");
    status.classList.add("d-none");
  }
}

/**
 * @description Shows or hides a info error message.
 * @method displayError
 * @param {string} listingId The error id.
 * @param {boolean} visible If true, shows the msg, otherwise hides it.
 * @param {string} [text] The message to show, or `undefined` if `visible` is false.
 */
export function displayError(listingId, visible, text) {
  /** @type {HTMLDivElement} */
  const status = document.querySelector(`article[data-id="${listingId}"] #errorBidMsg`);

  if (visible === true) {
    status.classList.remove("d-none");
    status.classList.add("d-flex");
    status.innerHTML = text;
  } else {
    status.classList.remove("d-flex");
    status.classList.add("d-none");
  }
}

/**
 * @description Show and hide the spinner element
 * @method displaySpinner
 * @param {string} listingId The listing id.
 * @param {boolean} spinnerVisible If true, shows the spinner, otherwise hides it.
 */
export function displaySpinner(listingId, spinnerVisible) {
  const spinner = document.querySelector(`article[data-id="${listingId}"] #spinnerBid`); //es: #spinnerListings, and others

  if (!spinner) {
    return;
  }

  if (spinnerVisible === true) {
    spinner.classList.remove("d-none");
    spinner.classList.add("d-flex");
  } else {
    spinner.classList.remove("d-flex");
    spinner.classList.add("d-none");
  }
}

/**
 * @description Create a new bid on listing.
 * @async
 * @function createBid
 * @param {number} amount The bid amount
 * @param {string} listingId The bid amount
 * @returns {Promise<createBidResponse|null|undefined>} If response is ok, return listings. If response is not ok, return null. Returns undefined for unexpected errors.
 */
async function createBid(listingId, amount) {
  try {
    displaySpinner(listingId, true);
    displayError(listingId, false);

    const url = API_BASE + API_BID_ON_LISTING(listingId);

    /** @type {createBidRequest} */
    const request = {
      amount: amount,
    };

    const response = await fetch(url, {
      headers: {
        Authorization: `Bearer ${load("token")}`,
        "X-Noroff-API-Key": API_KEY,
        "Content-Type": "application/json",
      },
      method: "POST",
      body: JSON.stringify(request),
    });

    if (response.ok) {
      /** @type {createBidResponse} */
      const bid = await response.json();

      return bid;
    }

    const eh = new ErrorHandler(response);
    const msg = await eh.getErrorMessage();
    displayError(listingId, true, msg);

    return null;
  } catch (ev) {
    displayError(listingId, true, "Something went wrong, try again!");
  } finally {
    displaySpinner(listingId, false);
  }
}

/**
 * @description Handle the form bid submit.
 * @method handleBidSubmit
 * @param {string} listingId The bid amount
 * @param {Event} ev
 */
export async function handleBidSubmit(listingId, ev) {
  displayError(listingId, false, "");

  try {
    const form = /** @type {HTMLFormElement} */ (ev.currentTarget);

    /** @type {HTMLInputElement} */
    const txtAmount = form.elements["amountBid"];

    const amount = txtAmount.valueAsNumber;

    const bid = await createBid(listingId, amount);

    if (bid) {
      statusMsg(listingId, true, "Well done! You have created a new bid.");

      setTimeout(() => {
        statusMsg(listingId, false, "");

        form.reset();
        displayListings();
      }, 4000);
    }
  } catch (ev) {
    displayError(listingId, true, "Could not create a bid!");
  }
}