MetaPhase-Consulting/State-TalentMAP

View on GitHub
src/Components/AvailableBidder/AvailableBidderTable/AvailableBidderTable.jsx

Summary

Maintainability
B
6 hrs
Test Coverage
F
50%
import { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import PropTypes from 'prop-types';
import { EMPTY_FUNCTION } from 'Constants/PropTypes';
import { availableBidderExport, availableBiddersFetchData } from 'actions/availableBidders';
import { filtersFetchData } from 'actions/filters/filters';
import ToggleButton from 'Components/ToggleButton';
import ExportButton from 'Components/ExportButton';
import InteractiveElement from 'Components/InteractiveElement';
import { get } from 'lodash';
import AvailableBidderRow from 'Components/AvailableBidder/AvailableBidderRow';
import Alert from 'Components/Alert/Alert';
import FA from 'react-fontawesome';
import { Tooltip } from 'react-tippy';
import shortid from 'shortid';
import { useMount, usePrevious } from 'hooks';

const AvailableBidderTable = props => {
  const { isInternalCDA, isAO, isPost } = props;

  // Local state
  // Toggle view state within Internal CDA version
  const [internalViewToggle, setInternalViewToggle] = useState(isInternalCDA);
  const [sort, setSort] = useState('Name');
  const [exportIsLoading, setExportIsLoading] = useState(false);

  // App state
  const biddersData = useSelector(state => state.availableBiddersFetchDataSuccess);
  const availableBiddersIsLoading = useSelector(state => state.availableBiddersFetchDataLoading);
  const availableBiddersHasErrored = useSelector(state => state.availableBiddersFetchDataErrored);
  const filtersIsLoading = useSelector(state => state.filtersIsLoading);
  const filterData = useSelector(state => state.filters);

  const isLoading = availableBiddersIsLoading || filtersIsLoading;
  const hasErrored = availableBiddersHasErrored;

  const alertTitle = !hasErrored ? 'Available Bidders List is Empty' : 'Error loading Available Bidders List';
  const alertBody = [
    !hasErrored ?
      {
        body: isInternalCDA ?
          'Please navigate to the CDO Client Profiles to begin searching and adding bidders.' :
          'Please wait for Internal CDAs to share available bidders.',
      } : {
        body: 'Please try again.',
      },
  ];


  const bureaus = (get(filterData, 'filters') || []).find(f => f.item.description === 'region');

  const bidders = isLoading ? [...new Array(10)] : get(biddersData, 'results', []);

  const prevSort = usePrevious(sort);

  // Actions
  const dispatch = useDispatch();

  useMount(() => {
    dispatch(availableBiddersFetchData(isInternalCDA, sort));
    dispatch(filtersFetchData(filterData, {}));
  });

  useEffect(() => {
    if (prevSort && sort && sort !== prevSort) {
      dispatch(availableBiddersFetchData(isInternalCDA, sort));
    }
  }, [sort]);

  let tableHeaders = isInternalCDA ? [
    'Name',
    'Status',
    isInternalCDA ? 'Step Letters' : undefined,
    'Skill',
    'Grade',
    'Languages',
    'TED',
    'Post',
    'CDO',
    'Updated',
    'Notes',
  ] : [
    'Name',
    'Skill',
    'Grade',
    'Languages',
    'TED',
    'Post',
    'CDO',
  ];

  tableHeaders = tableHeaders.filter(f => f);

  const getSortIcon = (header) => {
    let header$ = header;
    header$ = header$.replace('Updated', 'Update');
    if (header$ === sort) {
      return 'sort-asc';
    } else if (`-${header$}` === sort) {
      return 'sort-desc';
    }
    return 'sort';
  };

  const handleSort = (header) => {
    let header$ = header;
    header$ = header$.replace('Updated', 'Update');
    // Dynamically set the sort asc or desc('-')
    return header$ === sort ? setSort(`-${header$}`) : setSort(header$);
  };

  let title = '';
  if (isInternalCDA) {
    title = internalViewToggle ? 'Internal CDA View' : 'External CDA View';
  }

  const getTitleCount = () => {
    let bidderCountTitle = '';
    if (!isLoading) {
      if (isInternalCDA) {
        bidderCountTitle = internalViewToggle ? `(${bidders.length})` : `(${bidders.filter(b => get(b, 'available_bidder_details.is_shared')).length})`;
      } else {
        bidderCountTitle = `Shared Available Bidders (${bidders.length})`;
      }
    }
    return bidderCountTitle;
  };

  const exportBidders = () => {
    if (!isLoading) {
      setExportIsLoading(true);
      availableBidderExport(isInternalCDA && internalViewToggle, sort)
        .then(() => {
          setExportIsLoading(false);
        })
        .catch(() => {
          setExportIsLoading(false);
        });
    }
  };

  return (
    !bidders.length && !isLoading && !hasErrored ?
      <div className="usa-width-two-thirds">
        <Alert
          title={alertTitle}
          messages={alertBody}
        />
      </div>
      :
      <>
        <div className="ab-table-title-row">
          <h3>{title} {getTitleCount()}</h3>
          {
            !hasErrored &&
              <ExportButton
                onClick={exportBidders}
                isLoading={exportIsLoading}
                disabled={!bidders.length}
                text={internalViewToggle || !isInternalCDA ? 'Export' : 'Export External View'}
              />
          }
        </div>
        <div className={`usa-width-one-whole bidder-manager-bidders ${isInternalCDA ? 'internal ' : ''}ab-lower-section`}>
          {
            <table>
              <thead>
                <tr>
                  {
                    tableHeaders.map(item => (
                      item !== 'Languages' && item !== 'Notes' && item !== 'Step Letters' ?
                        <th
                          key={item}
                          scope="col"
                        >
                          <InteractiveElement onClick={() => handleSort(item)}>
                            {item} <FA name={getSortIcon(item)} />
                          </InteractiveElement>
                        </th>
                        :
                        <th
                          key={item}
                          scope="col"
                        >
                          {item}
                        </th>
                    ))
                  }
                  {
                    isInternalCDA &&
                      <th>
                        <div className="external-view-toggle">
                          <ToggleButton
                            labelTextLeft={
                              <Tooltip
                                title="Internal CDA View"
                                arrow
                                offset={-95}
                                position="top-end"
                                tabIndex="0"
                              >
                                <FA name="street-view" className={`fa-lg ${internalViewToggle ? 'active' : ''}`} />
                              </Tooltip>
                            }
                            labelTextRight={
                              <Tooltip
                                title="External CDA View"
                                arrow
                                offset={-95}
                                position="top-end"
                                tabIndex="0"
                              >
                                <FA name="building" className={`fa-lg ${!internalViewToggle ? 'active' : ''}`} />
                              </Tooltip>
                            }
                            checked={!internalViewToggle}
                            onChange={() => setInternalViewToggle(!internalViewToggle)}
                            onColor="#888888"
                            offColor="#888888"
                            onHandleColor="#FFFFFF"
                            offHandleColor="#FFFFFF"
                            boxShadow="0px 1px 5px rgba(0, 0, 0, 0.6)"
                            activeBoxShadow="0px 0px 1px 10px rgba(0, 0, 0, 0.2)"
                          />
                        </div>
                      </th>
                  }
                </tr>
              </thead>
              <tbody>
                {
                  bidders.map(bidder => (
                    <AvailableBidderRow
                      key={shortid.generate()}
                      bidder={bidder}
                      internalViewToggle={internalViewToggle}
                      isAO={isAO}
                      isInternalCDA={isInternalCDA}
                      isPost={isPost}
                      isLoading={isLoading}
                      bureaus={bureaus}
                      sort={sort}
                    />
                  ))
                }
              </tbody>
            </table>
          }
        </div>
        {
          hasErrored &&
          <div className="usa-width-one-whole">
            <Alert
              type={'error'}
              title={alertTitle}
              messages={alertBody}
            />
          </div>
        }
      </>
  );
};

AvailableBidderTable.propTypes = {
  isInternalCDA: PropTypes.bool,
  isAO: PropTypes.bool,
  isPost: PropTypes.bool,
};

AvailableBidderTable.defaultProps = {
  bidders: [],
  onSort: EMPTY_FUNCTION,
  onFilter: EMPTY_FUNCTION,
  isInternalCDA: false,
  isAO: false,
  isPost: false,
};

export default AvailableBidderTable;