MetaPhase-Consulting/State-TalentMAP

View on GitHub
src/Components/BureauPage/SearchPostAccess/SearchPostAccess.jsx

Summary

Maintainability
A
2 hrs
Test Coverage
F
1%
import { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { withRouter } from 'react-router';
import Picky from 'react-picky';
import FA from 'react-fontawesome';
import { renderSelectionList } from 'utilities';
import ProfileSectionTitle from 'Components/ProfileSectionTitle/ProfileSectionTitle';
import TotalResults from 'Components/TotalResults';
import { searchPostAccessFetchData, searchPostAccessFetchFilters, searchPostAccessRemove, searchPostAccessSaveSelections } from 'actions/searchPostAccess';
import Spinner from 'Components/Spinner';
import Alert from 'Components/Alert';
import CheckBox from 'Components/CheckBox';


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

  // State
  const userSelections = useSelector(state => state.searchPostAccessSelections);
  const searchPostAccessData = useSelector(state => state.searchPostAccess);
  const searchPostAccessFilters = useSelector(state => state.searchPostAccessFetchFilterData);
  const searchPostAccessFiltersLoading = useSelector(
    state => state.searchPostAccessFetchFiltersLoading);
  const searchPostAccessFetchDataLoading =
    useSelector(state => state.searchPostAccessFetchDataLoading);
  const searchPostAccessFetchDataError =
    useSelector(state => state.searchPostAccessFetchDataErrored);
  const searchPostAccessRemoveSuccess = useSelector(state => state.searchPostAccessRemoveSuccess);

  // Local State & Filters
  const [selectedBureaus, setSelectedBureaus] = useState(userSelections?.selectedBureaus || []);
  const [selectedLocations, setSelectedLocations] =
    useState(userSelections?.selectedLocations || []);
  const [selectedRoles, setSelectedRoles] = useState(userSelections?.selectedRoles || []);
  const [selectedOrgs, setSelectedOrgs] = useState(userSelections?.selectedOrgs || []);
  const [selectedPersons, setSelectedPersons] = useState(userSelections?.selectedPersons || []);
  const [selectedPositions, setSelectedPositions] =
    useState(userSelections?.selectedPositions || []);
  const [clearFilters, setClearFilters] = useState(false);
  const [checkedPostIds, setCheckedPostIds] = useState([]);
  const [selectAll, setSelectAll] = useState(false);


  const getCurrentInputs = () => ({
    selectedBureaus,
    selectedLocations,
    selectedOrgs,
    selectedRoles,
    selectedPersons,
    selectedPositions,
  });

  const getQuery = () => ({
    bureaus: selectedBureaus.map(bureau => (bureau?.code)),
    orgs: selectedOrgs.map(org => (org?.description)),
    locations: selectedLocations.map(loc => (loc?.code)),
    roles: selectedRoles.map(role => (role?.code)),
    persons: selectedPersons.map(person => (person?.code)),
    positions: selectedPositions.map(pos => (pos?.code)),
  });

  const filters = [
    selectedBureaus,
    selectedLocations,
    selectedOrgs,
    selectedRoles,
    selectedPersons,
    selectedPositions,
  ];

  const fetchAndSet = () => {
    const filterCount = filters.flat().length;
    setClearFilters(!!filterCount);
    if (filterCount > 1) {
      dispatch(searchPostAccessFetchData(getQuery()));
    }
    dispatch(searchPostAccessSaveSelections(getCurrentInputs()));
  };


  // Initial Render
  useEffect(() => {
    dispatch(searchPostAccessSaveSelections(getCurrentInputs()));
    dispatch(searchPostAccessFetchFilters());
  }, []);

  // Re-Render on Filter Selections
  useEffect(() => {
    setCheckedPostIds([]);
    setSelectAll(false);
    fetchAndSet();
  }, [
    selectedBureaus,
    selectedOrgs,
    selectedLocations,
    selectedRoles,
    selectedPersons,
    selectedPositions,
    searchPostAccessRemoveSuccess,
  ]);


  // Filter Options
  const bureauOptions = searchPostAccessFilters?.bureauFilters || [];
  const roleOptions = searchPostAccessFilters?.roleFilters || [];
  const locationOptions = searchPostAccessFilters?.locationFilters || [];
  const orgOptions = searchPostAccessFilters?.orgFilters || [];
  const personOptions = searchPostAccessFilters?.personFilters || [];
  const positionOptions = searchPostAccessFilters?.positionFilters || [];

  const resetFilters = () => {
    setSelectedPersons([]);
    setSelectedPositions([]);
    setSelectedBureaus([]);
    setSelectedOrgs([]);
    setSelectedLocations([]);
    setSelectedRoles([]);
    setCheckedPostIds([]);
    setSelectAll(false);
    setClearFilters(false);
  };

  // Overlay for error, info, and loading state
  const noResults = !searchPostAccessData?.length;
  const getOverlay = () => {
    let overlay;
    if (searchPostAccessFetchDataLoading) {
      overlay = <Spinner type="bureau-results" class="homepage-position-results" size="big" />;
    } else if (searchPostAccessFetchDataError) {
      overlay = <Alert type="error" title="Error loading results" messages={[{ body: 'Please try again.' }]} />;
    } else if (filters.flat().length < 2) {
      overlay = <Alert type="info" title="Select Filters" messages={[{ body: 'Please select at least 2 filters to search.' }]} />;
    } else if (noResults) {
      overlay = <Alert type="info" title="No results found" messages={[{ body: 'Please broaden your search criteria and try again.' }]} />;
    } else {
      return false;
    }
    return overlay;
  };

  const pickyProps = {
    numberDisplayed: 2,
    multiple: true,
    includeFilter: true,
    dropdownHeight: 255,
    renderList: renderSelectionList,
  };

  const submitRemoveAccess = () => {
    dispatch(searchPostAccessRemove(checkedPostIds));
  };

  const tableHeaderNames = ['Access Type', 'Bureau', 'Post/Org', 'Employee', 'Role', 'Position', 'Title'];

  const handleSelectAll = () => {
    if (!selectAll) {
      setSelectAll(true);
      setCheckedPostIds(
        searchPostAccessData?.map(post => post.id),
      );
    } else {
      setSelectAll(false);
      setCheckedPostIds([]);
    }
  };

  const handleSelectPost = (post => {
    if (checkedPostIds.includes(post.id)) {
      const filteredPosts = checkedPostIds.filter(x => x !== post.id);
      setCheckedPostIds(filteredPosts);
    } else {
      setCheckedPostIds([...checkedPostIds, post.id]);
    }
  });

  return (
    <div className="position-search">
      <div className="usa-grid-full position-search--header">
        <ProfileSectionTitle title="Search Post Access" icon="search-minus" className="xl-icon" />
        <div className="results-search-bar pt-20">
          <div className="filterby-container">
            <div className="filterby-label">Filter by:</div>
            <div className="filterby-clear">
              {
                clearFilters &&
                <button
                  className="unstyled-button"
                  onClick={resetFilters}
                >
                  <FA name="times" />
                  Clear Filters
                </button>
              }
            </div>
          </div>
          <div className="usa-width-one-whole position-search--filters--spa results-dropdown">

            { searchPostAccessFiltersLoading
              ?
              <Spinner type="post-access-filters" size="small" />
              :
              <>
                <div className="filter-div">
                  <div className="label">Person:</div>
                  <Picky
                    {...pickyProps}
                    placeholder="Person(s)"
                    value={selectedPersons}
                    options={personOptions}
                    onChange={setSelectedPersons}
                    valueKey="code"
                    labelKey="description"
                    disabled={searchPostAccessFiltersLoading}
                  />
                </div>
                <div className="filter-div">
                  <div className="label">Location:</div>
                  <Picky
                    {...pickyProps}
                    placeholder="Select Location(s)"
                    value={selectedLocations}
                    options={locationOptions}
                    onChange={setSelectedLocations}
                    valueKey="code"
                    labelKey="description"
                    disabled={searchPostAccessFiltersLoading}
                  />
                </div>
                <div className="filter-div">
                  <div className="label">Bureau:</div>
                  <Picky
                    {...pickyProps}
                    placeholder="Select Bureau(s)"
                    value={selectedBureaus}
                    options={bureauOptions}
                    onChange={setSelectedBureaus}
                    valueKey="code"
                    labelKey="description"
                    disabled={searchPostAccessFiltersLoading}
                  />
                </div>
                <div className="filter-div">
                  <div className="label">Organization:</div>
                  <Picky
                    {...pickyProps}
                    placeholder="Select Organization(s)"
                    value={selectedOrgs}
                    options={orgOptions}
                    onChange={setSelectedOrgs}
                    valueKey="code"
                    labelKey="description"
                    disabled={searchPostAccessFiltersLoading}
                  />
                </div>
                <div className="filter-div">
                  <div className="label">Position:</div>
                  <Picky
                    {...pickyProps}
                    placeholder="Position(s)"
                    value={selectedPositions}
                    options={positionOptions}
                    onChange={setSelectedPositions}
                    valueKey="code"
                    labelKey="description"
                    disabled={searchPostAccessFiltersLoading}
                  />
                </div>
                <div className="filter-div">
                  <div className="label">Role:</div>
                  <Picky
                    {...pickyProps}
                    placeholder="Select Role(s)"
                    value={selectedRoles}
                    options={roleOptions}
                    onChange={setSelectedRoles}
                    valueKey="code"
                    labelKey="description"
                    disabled={searchPostAccessFiltersLoading}
                  />
                </div>
              </>
            }

          </div>
        </div>
      </div>
      {
        getOverlay() ||
        <>
          <div className="usa-width-one-whole results-dropdown controls-container mb-10">
            <TotalResults
              total={searchPostAccessData.length}
              pageSize={'all'}
              suffix="Results"
              isHidden={searchPostAccessFetchDataLoading}
            />
          </div>
          <div className="usa-width-one-whole position-search--results">
            <div className="post-access-scroll-container">
              <table className="custom-table">
                <thead>
                  <tr>
                    <th className="checkbox-pos">
                      <CheckBox
                        checked={!selectAll}
                        onCheckBoxClick={handleSelectAll}
                      />
                    </th>
                    {
                      tableHeaderNames.map((item) => (
                        <th key={item}>{item}</th>
                      ))
                    }
                  </tr>
                </thead>
                <tbody>
                  {
                    searchPostAccessData?.map(post => (
                      <tr key={post.id}>
                        <td className="checkbox-pac checkbox-pos">
                          <CheckBox
                            value={checkedPostIds.includes(post.id)}
                            onCheckBoxClick={() => handleSelectPost(post)}
                          />
                        </td>
                        <td>{post?.access_type}</td>
                        <td>{post?.bureau}</td>
                        <td>{post?.post}</td>
                        <td>{post?.employee}</td>
                        <td>{post?.role}</td>
                        <td>{post?.position}</td>
                        <td>{post?.title}</td>
                      </tr>
                    ))
                  }
                </tbody>
              </table>
            </div>
          </div>
        </>
      }
      {
        !!checkedPostIds.length &&
          <div className="proposed-cycle-banner">
            {checkedPostIds.length} {checkedPostIds.length < 2 ? 'Position' : 'Positions'} Selected
            {
              !!checkedPostIds.length &&
              <button className="usa-button-secondary" onClick={submitRemoveAccess}>Remove Access</button>
            }
          </div>
      }
    </div>
  );
};

export default withRouter(SearchPostAccess);