MetaPhase-Consulting/State-TalentMAP

View on GitHub
src/Components/AdministratorPage/OrgStats/OrgStats.jsx

Summary

Maintainability
A
3 hrs
Test Coverage
F
4%
import { useEffect, useState } from 'react';
import { withRouter } from 'react-router';
import { useDispatch, useSelector } from 'react-redux';
import { sortBy, uniqBy } from 'lodash';
import Picky from 'react-picky';
import FA from 'react-fontawesome';
import Alert from 'Components/Alert';
import Spinner from 'Components/Spinner';
import ProfileSectionTitle from 'Components/ProfileSectionTitle';
import DefinitionList from 'Components/DefinitionList';
import { Row } from 'Components/Layout';
import { renderSelectionList } from 'utilities';
import OrgStatsCard from './OrgStatsCard';
import { orgStatsFetchData, orgStatsFiltersFetchData, saveOrgStatsSelections } from '../../../actions/orgStats';

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

  // ================= DATA RETRIEVAL =================
  const userSelections = useSelector(state => state.orgStatsSelections);

  const orgStatsData = useSelector(state => state.orgStats);
  const orgStatsData$ = orgStatsData?.results || [];
  const orgStatsSummary$ = orgStatsData?.bureau_summary || [];
  const orgStatsIsLoading = useSelector(state => state.orgStatsFetchDataLoading);
  const orgStatsError = useSelector(state => state.orgStatsFetchDataErrored);

  const filtersHasErrored = useSelector(state => state.orgStatsFiltersHasErrored);
  const filtersIsLoading = useSelector(state => state.orgStatsFiltersIsLoading);
  const orgStatsfilters = useSelector(state => state.orgStatsFilters);

  const bureaus = orgStatsfilters?.bureauFilters;
  const orgs = orgStatsfilters?.orgFilters;
  const cycles = orgStatsfilters?.cycleFilters;
  const bureauOptions = uniqBy(sortBy(bureaus, [(f) => f.description]), 'description');
  const orgOptions = uniqBy(sortBy(orgs, [(f) => f.description]), 'code');
  const cycleOptions = uniqBy(sortBy(cycles, [(f) => f.code]), 'code');
  // ================= FILTER =================

  const [clearFilters, setClearFilters] = useState(false);

  const [selectedBureaus, setSelectedBureaus] = useState(userSelections?.selectedBureaus || []);
  const [selectedOrgs, setSelectedOrgs] = useState(userSelections?.selectedOrgs || []);
  const [selectedCycles, setSelectedCycles] = useState(userSelections?.selectedBidCycle || []);

  const getCurrentInputs = () => ({
    selectedBureaus,
    selectedOrgs,
    selectedCycles,
  });

  const getQuery = () => ({
    bureaus: selectedBureaus.map(bureauObject => (bureauObject?.description)),
    orgs: selectedOrgs.map(orgObject => (orgObject?.code)),
    cycles: selectedCycles.map(cycleObject => (cycleObject?.code)),
  });


  const filterSelectionValid = () => {
    const fils = [
      selectedBureaus,
      selectedOrgs,
      selectedCycles,
    ];
    const a = [];
    fils.forEach(f => { if (f.length) { a.push(true); } });
    return a.length;
  };


  const fetchAndSet = () => {
    setClearFilters(filterSelectionValid() !== 0);

    if (filterSelectionValid() > 1) {
      dispatch(orgStatsFetchData(getQuery()));
      dispatch(saveOrgStatsSelections(getCurrentInputs()));
    }
  };

  useEffect(() => {
    dispatch(orgStatsFiltersFetchData());
  }, []);

  useEffect(() => {
    fetchAndSet();
  }, [
    selectedBureaus,
    selectedOrgs,
    selectedCycles,
  ]);

  const resetFilters = () => {
    setSelectedBureaus([]);
    setSelectedOrgs([]);
    setSelectedCycles([]);
    dispatch(saveOrgStatsSelections(getCurrentInputs()));
    setClearFilters(false);
  };

  // Overlay for error, info, and loading state
  const noResults = orgStatsData?.results?.length === 0;
  const getOverlay = () => {
    let overlay;
    if (orgStatsIsLoading || filtersIsLoading) {
      overlay = <Spinner type="bid-season-filters" class="homepage-position-results" size="big" />;
    } else if (orgStatsError || filtersHasErrored) {
      overlay = <Alert type="error" title="Error loading results" messages={[{ body: 'Please try again.' }]} />;
    } else if (noResults) {
      overlay = <Alert type="info" title="No results found" messages={[{ body: 'Please broaden your search criteria and try again.' }]} />;
    } else if (filterSelectionValid() < 2) {
      overlay = <Alert type="info" title="Select Filters" messages={[{ body: 'Please select at least 2 distinct filters to search.' }]} />;
    } else {
      return false;
    }
    return overlay;
  };

  return (filtersIsLoading ?
    <Spinner type="homepage-position-results" class="homepage-position-results" size="big" /> :
    <div className="bid-seasons-page position-search">
      <div className="usa-grid-full position-search--header">
        <ProfileSectionTitle title="Org Stats Search" icon="building" className="xl-icon" />
        <div className="filterby-container" >
          <div className="filterby-label">Filter by:</div>
          <span className="filterby-clear">
            {clearFilters &&
              <button className="unstyled-button" onClick={resetFilters}>
                <FA name="times" />
                Clear Filters
              </button>
            }
          </span>
        </div>
        <div className="usa-width-one-whole position-search--filters--pos-man results-dropdown">
          <div className="filter-div">
            <div className="label">Bureau:</div>
            <Picky
              placeholder="Select Bureau(s)"
              value={selectedBureaus}
              options={bureauOptions}
              onChange={setSelectedBureaus}
              numberDisplayed={2}
              multiple
              includeFilter
              dropdownHeight={255}
              renderList={renderSelectionList}
              valueKey="description"
              labelKey="description"
            />
          </div>
          <div className="filter-div">
            <div className="label">Organization:</div>
            <Picky
              placeholder="Select Organization(s)"
              value={selectedOrgs}
              options={orgOptions}
              onChange={setSelectedOrgs}
              numberDisplayed={2}
              multiple
              includeFilter
              dropdownHeight={255}
              renderList={renderSelectionList}
              valueKey="code"
              labelKey="description"
            />
          </div>
          <div className="filter-div">
            <div className="label">Cycle:</div>
            <Picky
              placeholder="Select Cycle(s)"
              value={selectedCycles}
              options={cycleOptions}
              onChange={setSelectedCycles}
              numberDisplayed={2}
              multiple
              includeFilter
              dropdownHeight={255}
              renderList={renderSelectionList}
              valueKey="code"
              labelKey="description"
            />
          </div>
        </div>
      </div>
      {getOverlay() ||
        <div className="bs-lower-section">
          {orgStatsData$?.map((data, index) => {
            const bureauSummary = orgStatsSummary$.find(s =>
              s.bureau_short_desc === data.bureau_short_desc);
            const currBureau = data.bureau_short_desc;
            const nextBureau = orgStatsData$[index + 1]?.bureau_short_desc;
            if (currBureau !== nextBureau) {
              const summaryBody = {
                // the alternative zero is to prevent the value from being falsy
                'Bureau: ': bureauSummary?.bureau_short_desc,
                'Total POS': bureauSummary?.total_pos || '0',
                'Total Filled': bureauSummary?.total_filled || '0',
                '% Filled': bureauSummary?.total_percent || '0',
                'Overseas POS': bureauSummary?.overseas_pos || '0',
                'Overseas Filled': bureauSummary?.overseas_filled || '0',
                '% Overseas': bureauSummary?.overseas_percent || '0',
                'Domestic POS': bureauSummary?.domestic_pos || '0',
                'Domestic Filled': bureauSummary?.domestic_filled || '0',
                '% Domestic': bureauSummary?.domestic_percent || '0',
              };
              return (
                <div>
                  <OrgStatsCard {...data} />
                  <Row fluid className="tabbed-card dark box-shadow-standard">
                    <div className="position-content pt-12">
                      <Row fluid className="position-content--section position-content--details condensed">
                        <DefinitionList
                          itemProps={{ excludeColon: true }}
                          items={summaryBody}
                        />
                      </Row>
                    </div>
                  </Row>
                </div>
              );
            }
            return <OrgStatsCard {...data} />;
          })}
        </div>
      }
    </div>
  );
};

OrgStats.propTypes = {
};

OrgStats.defaultProps = {
};

export default withRouter(OrgStats);