diff --git a/src/app/src/components/FilterSidebarExtendedSearch.jsx b/src/app/src/components/FilterSidebarExtendedSearch.jsx new file mode 100644 index 000000000..7246227ba --- /dev/null +++ b/src/app/src/components/FilterSidebarExtendedSearch.jsx @@ -0,0 +1,476 @@ +import React, { useEffect } from 'react'; +import { bool, func } from 'prop-types'; +import { connect } from 'react-redux'; +import InputLabel from '@material-ui/core/InputLabel'; +import Button from '@material-ui/core/Button'; +import CircularProgress from '@material-ui/core/CircularProgress'; +import ReactSelect from 'react-select'; +import Creatable from 'react-select/creatable'; +import Divider from '@material-ui/core/Divider'; +import { withStyles } from '@material-ui/core/styles'; +import uniq from 'lodash/uniq'; + +import ShowOnly from './ShowOnly'; + +import { + updateContributorTypeFilter, + updateParentCompanyFilter, + updateFacilityTypeFilter, + updateProcessingTypeFilter, + updateProductTypeFilter, + updateNumberofWorkersFilter, + updateNativeLanguageNameFilter, + updateBoundaryFilter, +} from '../actions/filters'; + +import { + fetchContributorTypeOptions, + fetchFacilityProcessingTypeOptions, + fetchProductTypeOptions, + fetchNumberOfWorkersOptions, +} from '../actions/filterOptions'; + +import { fetchFacilities } from '../actions/facilities'; + +import { showDrawFilter } from '../actions/ui'; + +import { + contributorOptionsPropType, + contributorTypeOptionsPropType, + facilityTypeOptionsPropType, + processingTypeOptionsPropType, + facilityProcessingTypeOptionsPropType, + productTypeOptionsPropType, + numberOfWorkerOptionsPropType, +} from '../util/propTypes'; + +import { filterSidebarStyles } from '../util/styles'; + +import { + getValueFromEvent, + mapDjangoChoiceTuplesValueToSelectOptions, +} from '../util/util'; + +const filterSidebarSearchTabStyles = theme => + Object.freeze({ + formStyle: Object.freeze({ + width: '100%', + marginBottom: '32px', + }), + inputLabelStyle: Object.freeze({ + fontFamily: theme.typography.fontFamily, + fontSize: '16px', + fontWeight: 500, + color: '#000', + transform: 'translate(0, -8px) scale(1)', + paddingBottom: '0.5rem', + }), + helpSubheadStyle: Object.freeze({ + fontFamily: theme.typography.fontFamily, + ontSize: '12px', + fontWeight: 500, + color: '#000', + paddingTop: '0.5rem', + paddingBottom: '0.5rem', + }), + selectStyle: Object.freeze({ + fontFamily: theme.typography.fontFamily, + }), + font: Object.freeze({ + fontFamily: `${theme.typography.fontFamily} !important`, + }), + reset: Object.freeze({ + marginLeft: '16px', + minWidth: '36px', + minHeight: '36px', + }), + ...filterSidebarStyles, + }); + +const CONTRIBUTORS = 'CONTRIBUTORS'; +const CONTRIBUTOR_TYPES = 'CONTRIBUTOR_TYPES'; +const PARENT_COMPANY = 'PARENT_COMPANY'; +const FACILITY_TYPE = 'FACILITY_TYPE'; +const PROCESSING_TYPE = 'PROCESSING_TYPE'; +const PRODUCT_TYPE = 'PRODUCT_TYPE'; +const NUMBER_OF_WORKERS = 'NUMBER_OF_WORKERS'; + +const mapFacilityTypeOptions = (fPTypes, pTypes) => { + let fTypes = []; + if (pTypes.length === 0) { + fTypes = fPTypes.map(type => type.facilityType); + } else { + // When there are processing types, only return the + // facility types that have those processing types + pTypes.forEach(pType => { + fPTypes.forEach(fPType => { + if (fPType.processingTypes.includes(pType.value)) { + fTypes = fTypes.concat(fPType.facilityType); + } + }); + }); + } + return mapDjangoChoiceTuplesValueToSelectOptions(uniq(fTypes.sort())); +}; + +const mapProcessingTypeOptions = (fPTypes, fTypes) => { + let pTypes = []; + if (fTypes.length === 0) { + pTypes = fPTypes.map(type => type.processingTypes).flat(); + } else { + // When there are facility types, only return the + // processing types that are under those facility types + fTypes.forEach(fType => { + fPTypes.forEach(fPType => { + if (fType.value === fPType.facilityType) { + pTypes = pTypes.concat(fPType.processingTypes); + } + }); + }); + } + return mapDjangoChoiceTuplesValueToSelectOptions(uniq(pTypes.sort())); +}; + +function FilterSidebarSearchTab({ + contributorOptions, + contributorTypeOptions, + facilityProcessingTypeOptions, + productTypeOptions, + numberOfWorkersOptions, + contributorTypes, + updateContributorType, + parentCompany, + updateParentCompany, + facilityType, + updateFacilityType, + processingType, + updateProcessingType, + productType, + updateProductType, + numberOfWorkers, + updateNumberOfWorkers, + fetchingFacilities, + fetchingExtendedOptions, + activateDrawFilter, + clearDrawFilter, + boundary, + embed, + classes, + fetchContributorTypes, + fetchFacilityProcessingType, + fetchProductType, + fetchNumberOfWorkers, +}) { + useEffect(() => { + if (!contributorTypeOptions.length) { + fetchContributorTypes(); + } + + if (!facilityProcessingTypeOptions.length) { + fetchFacilityProcessingType(); + } + + if (!productTypeOptions.length) { + fetchProductType(); + } + + if (!numberOfWorkersOptions.length) { + fetchNumberOfWorkers(); + } + }, []); + + if (fetchingExtendedOptions) { + return ( +
+ +
+ ); + } + + const boundaryButton = + boundary == null ? ( + + ) : ( + + ); + + return ( + <> +
+ + + Contributor Type + + + +
+
+ + Area + + {boundaryButton} +
+
+ +
+ The following filters are new to the OAR and may not return + complete results until we have more data +
+
+
+ + Parent Company + + +
+
+ + Facility Type + + +
+
+ + Processing Type + + +
+
+ + Product Type + + +
+
+ + Number of Workers + + +
+ + ); +} + +FilterSidebarSearchTab.propTypes = { + contributorOptions: contributorOptionsPropType.isRequired, + contributorTypeOptions: contributorTypeOptionsPropType.isRequired, + facilityProcessingTypeOptions: + facilityProcessingTypeOptionsPropType.isRequired, + productTypeOptions: productTypeOptionsPropType.isRequired, + numberOfWorkersOptions: numberOfWorkerOptionsPropType.isRequired, + updateContributorType: func.isRequired, + contributorTypes: contributorTypeOptionsPropType.isRequired, + parentCompany: contributorOptionsPropType.isRequired, + facilityType: facilityTypeOptionsPropType.isRequired, + processingType: processingTypeOptionsPropType.isRequired, + productType: productTypeOptionsPropType.isRequired, + numberOfWorkers: numberOfWorkerOptionsPropType.isRequired, + fetchingFacilities: bool.isRequired, + fetchingExtendedOptions: bool.isRequired, +}; + +function mapStateToProps({ + filterOptions: { + contributors: { + data: contributorOptions, + fetching: fetchingContributors, + }, + contributorTypes: { + data: contributorTypeOptions, + fetching: fetchingContributorTypes, + }, + facilityProcessingType: { + data: facilityProcessingTypeOptions, + fetching: fetchingFacilityProcessingType, + }, + productType: { + data: productTypeOptions, + fetching: fetchingProductType, + }, + numberOfWorkers: { + data: numberOfWorkersOptions, + fetching: fetchingNumberofWorkers, + }, + }, + filters: { + contributorTypes, + parentCompany, + facilityType, + processingType, + productType, + numberOfWorkers, + nativeLanguageName, + boundary, + }, + facilities: { + facilities: { data: facilities, fetching: fetchingFacilities }, + }, + embeddedMap: { embed }, +}) { + return { + contributorOptions, + contributorTypeOptions, + facilityProcessingTypeOptions, + productTypeOptions, + numberOfWorkersOptions, + contributorTypes, + parentCompany, + facilityType, + processingType, + productType, + numberOfWorkers, + nativeLanguageName, + fetchingFacilities, + facilities, + boundary, + fetchingExtendedOptions: + fetchingContributors || + fetchingContributorTypes || + fetchingFacilityProcessingType || + fetchingProductType || + fetchingNumberofWorkers, + embed: !!embed, + }; +} + +function mapDispatchToProps(dispatch) { + return { + updateContributorType: v => dispatch(updateContributorTypeFilter(v)), + updateParentCompany: v => dispatch(updateParentCompanyFilter(v)), + updateFacilityType: v => dispatch(updateFacilityTypeFilter(v)), + updateProcessingType: v => dispatch(updateProcessingTypeFilter(v)), + updateProductType: v => dispatch(updateProductTypeFilter(v)), + updateNumberOfWorkers: v => dispatch(updateNumberofWorkersFilter(v)), + updateNativeLanguageName: e => + dispatch(updateNativeLanguageNameFilter(getValueFromEvent(e))), + activateDrawFilter: () => dispatch(showDrawFilter(true)), + clearDrawFilter: () => { + dispatch(showDrawFilter(false)); + dispatch(updateBoundaryFilter(null)); + return dispatch(fetchFacilities({})); + }, + fetchContributorTypes: () => dispatch(fetchContributorTypeOptions()), + fetchFacilityProcessingType: () => + dispatch(fetchFacilityProcessingTypeOptions()), + fetchProductType: () => dispatch(fetchProductTypeOptions()), + fetchNumberOfWorkers: () => dispatch(fetchNumberOfWorkersOptions()), + }; +} + +export default connect( + mapStateToProps, + mapDispatchToProps, +)(withStyles(filterSidebarSearchTabStyles)(FilterSidebarSearchTab)); diff --git a/src/app/src/components/FilterSidebarSearchTab.jsx b/src/app/src/components/FilterSidebarSearchTab.jsx index 317d52144..c73cdf946 100644 --- a/src/app/src/components/FilterSidebarSearchTab.jsx +++ b/src/app/src/components/FilterSidebarSearchTab.jsx @@ -12,14 +12,12 @@ import InfoIcon from '@material-ui/icons/Info'; import Tooltip from '@material-ui/core/Tooltip'; import Popover from '@material-ui/core/Popover'; import ReactSelect from 'react-select'; -import Creatable from 'react-select/creatable'; -import Divider from '@material-ui/core/Divider'; import { withStyles } from '@material-ui/core/styles'; import get from 'lodash/get'; -import uniq from 'lodash/uniq'; import ShowOnly from './ShowOnly'; import FeatureFlag from './FeatureFlag'; +import FilterSidebarExtendedSearch from './FilterSidebarExtendedSearch'; import { updateFacilityFreeTextQueryFilter, @@ -34,14 +32,12 @@ import { updateNumberofWorkersFilter, updateNativeLanguageNameFilter, updateCombineContributorsFilterOption, - updateBoundaryFilter, - updatePPEFilter, resetAllFilters, } from '../actions/filters'; import { fetchFacilities } from '../actions/facilities'; -import { recordSearchTabResetButtonClick, showDrawFilter } from '../actions/ui'; +import { recordSearchTabResetButtonClick } from '../actions/ui'; import { contributorOptionsPropType, @@ -49,7 +45,6 @@ import { countryOptionsPropType, facilityTypeOptionsPropType, processingTypeOptionsPropType, - facilityProcessingTypeOptionsPropType, productTypeOptionsPropType, numberOfWorkerOptionsPropType, facilityCollectionPropType, @@ -60,12 +55,12 @@ import { filterSidebarStyles } from '../util/styles'; import { getValueFromEvent, makeSubmitFormOnEnterKeyPressFunction, - mapDjangoChoiceTuplesValueToSelectOptions, } from '../util/util'; import { FACILITIES_REQUEST_PAGE_SIZE, DEFAULT_SEARCH_TEXT, + EXTENDED_PROFILE_FLAG, } from '../util/constants'; const filterSidebarSearchTabStyles = theme => @@ -106,80 +101,28 @@ const filterSidebarSearchTabStyles = theme => const FACILITIES = 'FACILITIES'; const CONTRIBUTORS = 'CONTRIBUTORS'; -const CONTRIBUTOR_TYPES = 'CONTRIBUTOR_TYPES'; const LISTS = 'LISTS'; const COUNTRIES = 'COUNTRIES'; -const PARENT_COMPANY = 'PARENT_COMPANY'; -const FACILITY_TYPE = 'FACILITY_TYPE'; -const PROCESSING_TYPE = 'PROCESSING_TYPE'; -const PRODUCT_TYPE = 'PRODUCT_TYPE'; -const NUMBER_OF_WORKERS = 'NUMBER_OF_WORKERS'; - -const mapFacilityTypeOptions = (fPTypes, pTypes) => { - let fTypes = []; - if (pTypes.length === 0) { - fTypes = fPTypes.map(type => type.facilityType); - } else { - // When there are processing types, only return the - // facility types that have those processing types - pTypes.forEach(pType => { - fPTypes.forEach(fPType => { - if (fPType.processingTypes.includes(pType.value)) { - fTypes = fTypes.concat(fPType.facilityType); - } - }); - }); - } - return mapDjangoChoiceTuplesValueToSelectOptions(uniq(fTypes.sort())); -}; - -const mapProcessingTypeOptions = (fPTypes, fTypes) => { - let pTypes = []; - if (fTypes.length === 0) { - pTypes = fPTypes.map(type => type.processingTypes).flat(); - } else { - // When there are facility types, only return the - // processing types that are under those facility types - fTypes.forEach(fType => { - fPTypes.forEach(fPType => { - if (fType.value === fPType.facilityType) { - pTypes = pTypes.concat(fPType.processingTypes); - } - }); - }); - } - return mapDjangoChoiceTuplesValueToSelectOptions(uniq(pTypes.sort())); -}; const checkIfAnyFieldSelected = fields => fields.some(f => f.length !== 0); function FilterSidebarSearchTab({ contributorOptions, listOptions, - contributorTypeOptions, countryOptions, - facilityProcessingTypeOptions, - productTypeOptions, - numberOfWorkersOptions, resetFilters, facilityFreeTextQuery, updateFacilityFreeTextQuery, contributors, updateContributor, contributorTypes, - updateContributorType, countries, updateCountry, parentCompany, - updateParentCompany, facilityType, - updateFacilityType, processingType, - updateProcessingType, productType, - updateProductType, numberOfWorkers, - updateNumberOfWorkers, combineContributors, updateCombineContributors, fetchingFacilities, @@ -188,11 +131,6 @@ function FilterSidebarSearchTab({ fetchingOptions, submitFormOnEnterKeyPress, vectorTileFlagIsActive, - activateDrawFilter, - clearDrawFilter, - boundary, - ppe, - updatePPE, embed, fetchingLists, updateList, @@ -219,7 +157,6 @@ function FilterSidebarSearchTab({ contributorPopoverAnchorEl, setContributorPopoverAnchorEl, ] = useState(null); - const [ppePopoverAnchorEl, setPpePopoverAnchorEl] = useState(null); const [expand, setExpand] = useState( checkIfAnyFieldSelected(extendedFields), ); @@ -304,38 +241,6 @@ function FilterSidebarSearchTab({ ); - const ppeInfoPopoverContent = ( -
-

- Personal protective equipment (PPE) includes masks, gloves, - gowns, visors and other equipment. -

-
- ); - - const boundaryButton = - boundary == null ? ( - - ) : ( - - ); - const expandButton = expand ? (