Source

components/Forms/AddToCartForm/AddToCartForm.js

import React, { useState } from 'react';
import { useDispatch } from 'react-redux';

import { ReactComponent as AddToCartIcon } from 'assets/icons/cartIcons/add.svg';
import { fetchProductToCart } from 'axios/cart/requests';
import Button from 'components/UI/Button';
import Input from 'components/UI/Input';
import Section from 'hoc/Section';
import { showAlert } from 'store/actions/alert';
import { addToCart } from 'store/actions/cart';

import classes from './AddToCartForm.module.css';
import { initialFormControls, validateControl } from './formHelpers';

/**
 * A form for adding new products to the cart
 * @category Application
 * @subcategory Elements
 * @component AddToCartForm
 * @returns {jsx} add to cart form
 * @see Input
 * @see Section
 * @see Button
 */

const AddToCartForm = () => {
  const dispatch = useDispatch();

  // defining local state for form's values
  const [controls, setControls] = useState({
    isFormValid: false,
    formControls: initialFormControls,
  });

  /**
   * Submit handler
   * @memberof AddToCartForm
   * @inner
   * @function submitHandler
   * @param {object} event
   */

  const submitHandler = (event) => {
    event.preventDefault();
  };

  /**
   * Inout change handler + validation
   * @memberof submitHandler
   * @inner
   * @function onChangeHandler
   * @param {object} event
   * @param {string} controlName
   */

  const onChangeHandler = (event, controlName) => {
    // getting targeted form controls from the state
    const formControls = { ...controls.formControls };
    const control = { ...formControls[controlName] };

    // gathering and validating targeted input value
    control.value = event.target.value;
    control.touched = true;
    control.valid = validateControl(control.value, control.validation);
    formControls[controlName] = control;

    let isFormValid = true;

    Object.keys(formControls).forEach((name) => {
      isFormValid = formControls[name].valid && isFormValid;
    });

    // updating form's local state
    setControls({ formControls, isFormValid });
  };

  /**
   * Create product handler
   * @memberof AddToCartForm
   * @inner
   * @function createProductHandler
   * @see module:Requests~fetchProductToCart
   * @see module:CartActions~addToCart
   * @see module:AlertActions~showAlert
   */

  const createProductHandler = async () => {
    // creating a product by getting form's values from the local state
    const product = {
      name: controls.formControls.productName.value,
      pictureUrl: controls.formControls.productUrl.value,
      price: +controls.formControls.productPrice.value,
      quantity: 1,
    };

    try {
      const response = await fetchProductToCart(product);

      product.id = response.data.name;

      dispatch(addToCart(product));
      dispatch(
        showAlert({
          alertType: 'success',
          alertMessage: 'Product added to your order',
        })
      );
    } catch (error) {
      dispatch(
        showAlert({
          alertType: 'error',
          alertMessage: `Could not fetch product to cart: ${error.message}`,
        })
      );
    }

    setControls({
      isFormValid: false,
      formControls: initialFormControls,
    });
  };

  /**
   * Rendering all needed inputs
   * @memberof AddToCartForm
   * @inner
   * @function renderInputs
   * @returns {jsx} inputs
   */

  const renderInputs = () => {
    return Object.keys(controls.formControls).map((controlName, index) => {
      const control = controls.formControls[controlName];
      return (
        <Input
          key={control + index}
          type={control.type}
          value={control.value}
          valid={control.valid}
          touched={control.touched}
          label={control.label}
          errorMessage={control.errorMessage}
          shouldValidate={!!control.validation}
          onChange={(event) => onChangeHandler(event, controlName)}
        />
      );
    });
  };

  return (
    <Section>
      <form
        className={classes.AddToCartForm}
        onSubmit={(event) => submitHandler(event)}
      >
        <h3>Create Product</h3>
        {renderInputs()}
        <Button
          disabled={!controls.isFormValid}
          onClick={() => createProductHandler()}
          dataTestId='create-product-btn'
        >
          <span>add to cart</span>
          <AddToCartIcon width='20' height='20' />
        </Button>
      </form>
    </Section>
  );
};

export default AddToCartForm;