diff --git a/src/react/src/Routes.jsx b/src/react/src/Routes.jsx index 49d50cc6c..dd5e45274 100644 --- a/src/react/src/Routes.jsx +++ b/src/react/src/Routes.jsx @@ -31,6 +31,7 @@ import ExternalRedirect from './components/ExternalRedirect'; import Facilities from './components/Facilities'; import ContributeProductionLocation from './components/Contribute/ContributeProductionLocation'; import SearchByOsIdResult from './components/Contribute/SearchByOsIdResult'; +import SearchByNameAndAddressResult from './components/Contribute/SearchByNameAndAddressResult'; import { sessionLogin } from './actions/auth'; import { fetchFeatureFlags } from './actions/featureFlags'; @@ -57,6 +58,7 @@ import { InfoPaths, contributeProductionLocationRoute, searchByOsIdResultRoute, + searchByNameAndAddressResultRoute, } from './util/constants'; class Routes extends Component { @@ -84,7 +86,11 @@ class Routes extends Component {
{!embed ? : null} -
+
+ { - dispatch(startFetchingSingleProductionLocation()); + dispatch(startFetchSingleProductionLocation()); return apiRequest .get(makeGetProductionLocationByOsIdURL(osID)) .then(({ data }) => - dispatch(completeFetchingSingleProductionLocation(data)), + dispatch(completeFetchSingleProductionLocation(data)), ) .catch(err => dispatch( logErrorAndDispatchFailure( err, 'An error prevented fetching data about that production location', - failFetchingSingleProductionLocation, + failFetchSingleProductionLocation, + ), + ), + ); + }; +} + +const mockedProductionLocations = [ + { + sector: ['Apparel'], + location_type: ['Contractor', 'Logo Application'], + name: 'Robinson Manufacturing Company Dayton', + parent_company: 'Robinson', + claim_status: 'unclaimed', + number_of_workers: { + max: 53, + min: 53, + }, + product_type: ['Accessories', 'Decoration'], + coordinates: { + lat: 35.4872298, + lng: -85.0189463, + }, + country: { + name: 'United States', + numeric: '840', + alpha_3: 'USA', + alpha_2: 'US', + }, + address: '798 Market Street, Dayton, Dayton, Tennessee 37321', + os_id: 'US2019085AACCK0', + processing_type: ['Contractor', 'Logo Application'], + }, + { + sector: ['Apparel'], + location_type: ['Finished Goods'], + name: 'Robinson Manufacturing Company, Dayton', + parent_company: 'ROBINSON', + claim_status: 'unclaimed', + number_of_workers: { + max: 58, + min: 58, + }, + product_type: ['APPAREL', 'NIKE'], + coordinates: { + lat: 35.5118656, + lng: -85.0064775, + }, + country: { + name: 'United States', + numeric: '840', + alpha_3: 'USA', + alpha_2: 'US', + }, + address: '1184 Broadway Dayton Tennessee 37321', + os_id: 'US2022082XJ6DVN', + processing_type: ['Finished Goods'], + }, + { + sector: ['Consumer Products', 'General Merchandise'], + location_type: ['Manufacturing', 'Production', 'Logo Application'], + name: 'Robinson Manufacturing', + claim_status: 'unclaimed', + product_type: ['Accessories', 'Loungewear', 'Sleepwear', 'Underwear'], + coordinates: { + lat: 35.4872298, + lng: -85.0189463, + }, + country: { + name: 'United States', + numeric: '840', + alpha_3: 'USA', + alpha_2: 'US', + }, + address: + '798 S. Market Street, Dayton, Tennessee, 37321, United States', + os_id: 'US2024275MWQQ62', + processing_type: ['Manufacturing', 'Production', 'Logo Application'], + }, + { + sector: ['Agriculture', 'Farming'], + name: 'GRUBER MANUFACTURING, INC', + claim_status: 'unclaimed', + coordinates: { + lat: 39.6900755, + lng: -121.8560144, + }, + country: { + name: 'United States', + numeric: '840', + alpha_3: 'USA', + alpha_2: 'US', + }, + address: '2462 Dayton Road, CHICO, CA, 95928-8225', + os_id: 'US2024299KEN8HK', + }, + { + sector: ['Chemicals', 'Commodities', 'Waste Management'], + location_type: [ + 'Onsite Chemical Disposal', + 'Offsite Chemical Disposal', + ], + name: 'CPCA MANUFACTURING LLC', + claim_status: 'unclaimed', + coordinates: { + lat: 39.762, + lng: -84.227, + }, + country: { + name: 'United States', + numeric: '840', + alpha_3: 'USA', + alpha_2: 'US', + }, + address: '750 ROSEDALE DR, DAYTON OHIO 45402 (MONTGOMERY)', + os_id: 'US2024212DV02QP', + processing_type: [ + 'Onsite Chemical Disposal', + 'Offsite Chemical Disposal', + ], + }, + { + sector: ['Apparel', 'Apparel Accessories'], + name: 'E T Manufacturing & Sales, Inc.', + claim_status: 'unclaimed', + coordinates: { + lat: 40.8727141, + lng: -74.1177198, + }, + country: { + name: 'United States', + numeric: '840', + alpha_3: 'USA', + alpha_2: 'US', + }, + address: '90 Dayton Ave, Passaic, NJ, 07055', + os_id: 'US2024275HE0B6E', + }, + { + sector: ['Chemicals', 'Commodities', 'Waste Management'], + location_type: [ + 'Offsite Chemical Disposal', + 'Onsite Chemical Disposal', + ], + name: 'HOHMAN PLATING & MANUFACTURING INC', + claim_status: 'unclaimed', + coordinates: { + lat: 39.784, + lng: -84.185, + }, + country: { + name: 'United States', + numeric: '840', + alpha_3: 'USA', + alpha_2: 'US', + }, + address: '814 HILLROSE AVE, DAYTON OHIO 45404 (MONTGOMERY)', + os_id: 'US2024212GE5H1A', + processing_type: [ + 'Offsite Chemical Disposal', + 'Onsite Chemical Disposal', + ], + }, + { + sector: ['Health', 'Medical Equipment & Services'], + name: 'GEM City Enginnering and Manufacturing Corporation', + claim_status: 'unclaimed', + coordinates: { + lat: 39.7842949, + lng: -84.1911605, + }, + country: { + name: 'United States', + numeric: '840', + alpha_3: 'USA', + alpha_2: 'US', + }, + address: '401 Leo St Dayton, Ohio 45404', + os_id: 'US2024283CJQHGK', + }, + { + sector: ['Chemicals', 'Commodities', 'Waste Management'], + location_type: [ + 'Onsite Chemical Disposal', + 'Offsite Chemical Disposal', + ], + name: 'MAYDAY MANUFACTURING CO', + claim_status: 'unclaimed', + coordinates: { + lat: 33.22, + lng: -97.174, + }, + country: { + name: 'United States', + numeric: '840', + alpha_3: 'USA', + alpha_2: 'US', + }, + address: '3100 JIM CHRISTAL RD, DENTON TEXAS 76207 (DENTON)', + os_id: 'US2024213ZB3ZHK', + processing_type: [ + 'Onsite Chemical Disposal', + 'Offsite Chemical Disposal', + ], + }, + { + sector: ['Health', 'Healthcare', 'Pharmaceuticals'], + name: 'Kobayashi America Manufacturing, LLC', + parent_company: 'Kobayashi Consumer Products LLC', + claim_status: 'unclaimed', + coordinates: { + lat: 34.7045473, + lng: -84.9496968, + }, + country: { + name: 'United States', + numeric: '840', + alpha_3: 'USA', + alpha_2: 'US', + }, + address: '245 Kraft Dr Dalton, Georgia 30721', + os_id: 'US2023084VCWT7Z', + }, +]; + +export function fetchProductionLocations() { + return async dispatch => { + dispatch(startFetchProductionLocations()); + + // TODO: Replace the mock implementation with an actual API call as part of https://opensupplyhub.atlassian.net/browse/OSDEV-1175 + return new Promise(resolve => { + setTimeout( + () => resolve({ data: mockedProductionLocations }), + 1000, + ); + }) + .then(({ data }) => + dispatch(completeFetchProductionLocations(data)), + ) + .catch(err => + dispatch( + logErrorAndDispatchFailure( + err, + 'An error prevented fetching production locations', + failFetchProductionLocations, ), ), ); diff --git a/src/react/src/components/Contribute/ConfirmNotFoundLocationDialog.jsx b/src/react/src/components/Contribute/ConfirmNotFoundLocationDialog.jsx new file mode 100644 index 000000000..662bceeb7 --- /dev/null +++ b/src/react/src/components/Contribute/ConfirmNotFoundLocationDialog.jsx @@ -0,0 +1,108 @@ +import React from 'react'; +import { withStyles } from '@material-ui/core/styles'; +import Dialog from '@material-ui/core/Dialog'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import DialogActions from '@material-ui/core/DialogActions'; +import Button from '@material-ui/core/Button'; +import IconButton from '@material-ui/core/IconButton'; +import CloseIcon from '@material-ui/icons/Close'; +import Typography from '@material-ui/core/Typography'; + +const makeConfirmNotFoundLocationDialogStyles = theme => + Object.freeze({ + dialogPaperStyles: Object.freeze({ + borderRadius: 0, + padding: '24px 80px 42px', + }), + closeButtonStyles: { + position: 'absolute', + right: '16px', + top: '16px', + color: '#000', + }, + dialogTitleStyles: Object.freeze({ + fontSize: '32px', + lineHeight: '32px', + fontWeight: theme.typography.fontWeightSemiBoldPlus, + textAlign: 'center', + padding: 0, + }), + dialogActionsStyles: Object.freeze({ + display: 'flex', + justifyContent: 'center', + gap: '16px', + }), + buttonBaseStyles: Object.freeze({ + textTransform: 'none', + border: 'none', + height: '48px', + width: '309px', + }), + buttonLabelStyles: Object.freeze({ + fontSize: '18px', + lineHeight: '22px', + fontWeight: theme.typography.fontWeightExtraBold, + }), + addLocationButtonStyles: Object.freeze({ + backgroundColor: theme.palette.action.main, + '&:hover': { + backgroundColor: theme.palette.action.dark, + }, + }), + }); + +const ConfirmNotFoundLocationDialog = ({ + confirmDialogIsOpen, + handleConfirmDialogClose, + classes, +}) => ( + + + + + + + Are you sure you have reviewed the entire list and could not + find the production location? + + + + + + + +); + +export default withStyles(makeConfirmNotFoundLocationDialogStyles)( + ConfirmNotFoundLocationDialog, +); diff --git a/src/react/src/components/Contribute/ProductionLocationDetails.jsx b/src/react/src/components/Contribute/ProductionLocationDetails.jsx new file mode 100644 index 000000000..daf8c66d2 --- /dev/null +++ b/src/react/src/components/Contribute/ProductionLocationDetails.jsx @@ -0,0 +1,100 @@ +import React from 'react'; +import { string, arrayOf, object } from 'prop-types'; +import { Typography } from '@material-ui/core'; +import { withStyles } from '@material-ui/core/styles'; +import PreviousOsIdTooltip from './PreviousOsIdTooltip'; +import COLOURS from '../../util/COLOURS'; + +const makeProductionLocationDetailsStyles = theme => ({ + locationDetailsStyles: Object.freeze({ + margin: '24px 0', + }), + locationNameStyles: Object.freeze({ + fontSize: '36px', + lineHeight: '44px', + fontWeight: theme.typography.fontWeightBold, + }), + locationCurrentOsIdStyles: Object.freeze({ + fontSize: '16px', + lineHeight: '20px', + fontWeight: theme.typography.fontWeightBold, + marginTop: '8px', + }), + locationHistoricalOsIdStyles: Object.freeze({ + fontSize: '14px', + lineHeight: '20px', + fontWeight: theme.typography.fontWeightBold, + color: COLOURS.DARK_GREY, + marginTop: '8px', + }), + locationAddressContainerStyles: Object.freeze({ + display: 'flex', + flexDirection: 'column', + marginTop: '12px', + }), + locationAddressStyles: Object.freeze({ + fontSize: '16px', + lineHeight: '20px', + fontWeight: theme.typography.fontWeightSemiBold, + }), +}); + +const ProductionLocationDetails = ({ + osId, + name, + address, + countryName, + historicalOsIds, + classes, +}) => { + const historicalOsIdsNotEmpty = + Array.isArray(historicalOsIds) && historicalOsIds.length > 0; + + return ( +
+ + {name} + + + {historicalOsIdsNotEmpty ? 'Current OS ID:' : 'OS ID:'} {osId} + + {historicalOsIdsNotEmpty && + historicalOsIds.map(historicalOsId => ( + + Previous OS ID: {historicalOsId} + + ))} +
+ + {address} + + + {countryName} + +
+
+ ); +}; + +ProductionLocationDetails.defaultProps = { + historicalOsIds: [], +}; + +ProductionLocationDetails.propTypes = { + osId: string.isRequired, + name: string.isRequired, + address: string.isRequired, + countryName: string.isRequired, + historicalOsIds: arrayOf(string), + classes: object.isRequired, +}; + +export default withStyles(makeProductionLocationDetailsStyles)( + ProductionLocationDetails, +); diff --git a/src/react/src/components/Contribute/SearchByNameAndAddressNotFoundResult.jsx b/src/react/src/components/Contribute/SearchByNameAndAddressNotFoundResult.jsx new file mode 100644 index 000000000..e78fa287f --- /dev/null +++ b/src/react/src/components/Contribute/SearchByNameAndAddressNotFoundResult.jsx @@ -0,0 +1,110 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import Typography from '@material-ui/core/Typography'; +import { withStyles } from '@material-ui/core/styles'; +import { object } from 'prop-types'; +import history from '../../util/history'; +import { contributeProductionLocationRoute } from '../../util/constants'; + +const makeSearchByNameAndAddressNotFoundResultStyles = theme => + Object.freeze({ + contentWrapperStyles: Object.freeze({ + maxWidth: '601px', + margin: '94px auto 166px', + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + }), + titleStyles: Object.freeze({ + fontWeight: theme.typography.fontWeightExtraBold, + fontSize: '56px', + lineHeight: '60px', + marginBottom: '16px', + textAlign: 'center', + }), + descriptionStyles: Object.freeze({ + fontSize: '18px', + lineHeight: '21px', + marginBottom: '32px', + textAlign: 'center', + }), + controlsContainerStyles: Object.freeze({ + display: 'flex', + gap: '24px', + }), + buttonBaseStyles: Object.freeze({ + textTransform: 'none', + border: 'none', + height: '49px', + width: '256px', + }), + buttonLabelStyles: Object.freeze({ + fontSize: '18px', + lineHeight: '22px', + fontWeight: theme.typography.fontWeightExtraBold, + }), + addLocationButtonStyles: Object.freeze({ + backgroundColor: theme.palette.action.main, + '&:hover': { + backgroundColor: theme.palette.action.dark, + }, + }), + }); + +const SearchByNameAndAddressNotFoundResult = ({ classes }) => { + const handleSearchAgain = () => { + // clearProductionLocations(); + history.push(`${contributeProductionLocationRoute}?tab=name-address`); + }; + + const handleAddNewLocation = () => { + // clearProductionLocations(); + console.log('Add a new location'); + }; + + return ( +
+ + Search returned no results + + + We could not find any results that match your search criteria. + If you think the production location you are looking for is + already on OS Hub, check the information you entered and try + again. If you still don't find any results, add a new + production location. + +
+ + +
+
+ ); +}; + +SearchByNameAndAddressNotFoundResult.propTypes = { + classes: object.isRequired, +}; + +export default withStyles(makeSearchByNameAndAddressNotFoundResultStyles)( + SearchByNameAndAddressNotFoundResult, +); diff --git a/src/react/src/components/Contribute/SearchByNameAndAddressResult.jsx b/src/react/src/components/Contribute/SearchByNameAndAddressResult.jsx new file mode 100644 index 000000000..d626a2d0a --- /dev/null +++ b/src/react/src/components/Contribute/SearchByNameAndAddressResult.jsx @@ -0,0 +1,101 @@ +import React, { useEffect } from 'react'; +import { array, bool, func, object } from 'prop-types'; +import { connect } from 'react-redux'; +import { withStyles } from '@material-ui/core/styles'; +import CircularProgress from '@material-ui/core/CircularProgress'; +import BackToSearchButton from './BackToSearchButton'; +import SearchByNameAndAddressSuccessResult from './SearchByNameAndAddressSuccessResult'; +import SearchByNameAndAddressNotFoundResult from './SearchByNameAndAddressNotFoundResult'; +import { contributeProductionLocationRoute } from '../../util/constants'; +import history from '../../util/history'; +import { fetchProductionLocations } from '../../actions/contributeProductionLocation'; + +const makeSearchByNameAndAddressResultStyles = () => + Object.freeze({ + circularProgressContainerStyles: Object.freeze({ + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + height: 'calc(100vh - 116px)', + }), + backToSearchButtonContainerStyles: Object.freeze({ + padding: '48px 5% 0 5%', + }), + }); + +const SearchByNameAndAddressResult = ({ + data, + fetching, + fetchLocations, + classes, +}) => { + console.log('data >>>', data); + console.log('fetching >>>', fetching); + useEffect(() => { + fetchLocations(); + }, [fetchLocations]); + + const handleBackToSearchByNameAddress = () => { + // clearProductionLocations(); + history.push(`${contributeProductionLocationRoute}?tab=name-address`); + }; + + const count = data?.data?.length || 0; + + if (fetching) { + return ( +
+ +
+ ); + } + + return ( + <> +
+ +
+ {count > 0 ? ( + + ) : ( + + )} + + ); +}; + +SearchByNameAndAddressResult.defaultProps = { + data: [], + fetching: false, +}; + +SearchByNameAndAddressResult.propTypes = { + data: array, + fetching: bool, + fetchLocations: func.isRequired, + classes: object.isRequired, +}; + +const mapStateToProps = ({ + contributeProductionLocation: { + productionLocations: { data, fetching }, + }, +}) => ({ + data, + fetching, +}); +const mapDispatchToProps = dispatch => ({ + fetchLocations: () => dispatch(fetchProductionLocations()), +}); + +export default connect( + mapStateToProps, + mapDispatchToProps, +)( + withStyles(makeSearchByNameAndAddressResultStyles)( + SearchByNameAndAddressResult, + ), +); diff --git a/src/react/src/components/Contribute/SearchByNameAndAddressSuccessResult.jsx b/src/react/src/components/Contribute/SearchByNameAndAddressSuccessResult.jsx new file mode 100644 index 000000000..3e2a9d87c --- /dev/null +++ b/src/react/src/components/Contribute/SearchByNameAndAddressSuccessResult.jsx @@ -0,0 +1,223 @@ +import React, { useState, useEffect } from 'react'; +import { withStyles } from '@material-ui/core/styles'; +import Button from '@material-ui/core/Button'; +import Typography from '@material-ui/core/Typography'; +import ConfirmNotFoundLocationDialog from './ConfirmNotFoundLocationDialog'; +import ProductionLocationDetails from './ProductionLocationDetails'; +import COLOURS from '../../util/COLOURS'; + +const makeSearchByNameAndAddressSuccessResultStyles = theme => + Object.freeze({ + searchResultsContainerStyles: Object.freeze({ + padding: '24px 5% 0 5%', + }), + titleStyles: Object.freeze({ + fontWeight: theme.typography.fontWeightExtraBold, + fontSize: '56px', + lineHeight: '60px', + marginBottom: '16px', + }), + descriptionStyles: Object.freeze({ + fontSize: '18px', + lineHeight: '21px', + marginBottom: '32px', + maxWidth: '656px', + }), + subTitleStyles: Object.freeze({ + fontWeight: theme.typography.fontWeightSemiBoldPlus, + fontSize: '36px', + lineHeight: '44px', + margin: '32px 0 24px 0', + }), + resultsInfoContainerStyles: Object.freeze({ + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + marginBottom: '24px', + }), + resultsInfoStyles: Object.freeze({ + fontSize: '16px', + lineHeight: '20px', + fontWeight: theme.typography.fontWeightBold, + }), + resultsSortStyles: Object.freeze({ + fontSize: '16px', + lineHeight: '20px', + }), + resultContainer: Object.freeze({ + display: 'flex', + justifyContent: 'space-between', + gap: '10px', + padding: '12px', + borderBottom: `1px solid ${COLOURS.ACCENT_GREY}`, + '&:hover': { + backgroundColor: COLOURS.LIGHT_GREY, + }, + '&:last-child': { + marginBottom: '32px', + }, + }), + buttonBaseStyles: Object.freeze({ + height: '48px', + textTransform: 'none', + backgroundColor: theme.palette.action.main, + color: theme.palette.common.black, + '&:hover': { + backgroundColor: theme.palette.action.dark, + }, + }), + selectButtonStyles: Object.freeze({ + minWidth: '100px', + }), + selectButtonLabelStyles: Object.freeze({ + fontSize: '16px', + lineHeight: '20px', + fontWeight: theme.typography.fontWeightExtraBold, + }), + notFoundButtonOuterContainerStyles: Object.freeze({ + position: 'sticky', + bottom: -108, + height: '188px', + }), + notFoundButtonContainerStyles: Object.freeze({ + display: 'flex', + alignItems: 'center', + boxShadow: '0px -2px 12px 0px #00000014', + position: 'sticky', + bottom: 0, + padding: '0 5%', + height: '80px', + backgroundColor: '#fff', + }), + notFoundButtonContainerScrolledStyles: Object.freeze({ + boxShadow: 'none', + }), + notFoundButtonStyles: Object.freeze({ + width: '275px', + }), + notFoundButtonLabelStyles: Object.freeze({ + fontSize: '18px', + lineHeight: '22px', + fontWeight: theme.typography.fontWeightExtraBold, + }), + }); + +const SearchByNameAndAddressSuccessResult = ({ data, classes }) => { + const [confirmDialogIsOpen, setConfirmDialogIsOpen] = useState(false); + const [isScrolledToBottom, setIsScrolledToBottom] = useState(false); + + useEffect(() => { + const handleScroll = () => { + const isAtBottom = + window.innerHeight + window.scrollY >= + document.getElementById('mainPanel').offsetHeight; + setIsScrolledToBottom(isAtBottom); + }; + + window.addEventListener('scroll', handleScroll); + return () => { + window.removeEventListener('scroll', handleScroll); + }; + }, []); + + const handleSelectLocation = () => { + console.log('Select'); + }; + + const handleNotFoundLocation = () => { + setConfirmDialogIsOpen(true); + console.log('I don’t see my Location'); + }; + + const handleConfirmDialogClose = () => { + setConfirmDialogIsOpen(false); + console.log('Close'); + }; + + const count = data?.data?.length || 0; + + return ( + <> + +
+ + Search results + + + We found results that closely match your search criteria. + Since names and addresses on OS Hub are user-provided, there + may be slight differences between what you entered and + what's shown below. Find the best match for the + production location you are looking for in the list below, + click “Select” to edit the name, address and country, and to + add more information. + + + Locations + +
+ + {count} results + + + Sort By: Best match + +
+
+ {data?.data && + data?.data.map(location => ( +
+ + +
+ ))} +
+
+ +
+
+ +
+
+ + ); +}; + +export default withStyles(makeSearchByNameAndAddressSuccessResultStyles)( + SearchByNameAndAddressSuccessResult, +); diff --git a/src/react/src/components/Contribute/SearchByOsIdResult.jsx b/src/react/src/components/Contribute/SearchByOsIdResult.jsx index f04b4b76e..ad7bc35cc 100644 --- a/src/react/src/components/Contribute/SearchByOsIdResult.jsx +++ b/src/react/src/components/Contribute/SearchByOsIdResult.jsx @@ -1,7 +1,6 @@ import React, { useEffect } from 'react'; -import { useHistory, useLocation } from 'react-router-dom'; +import { useHistory, useParams } from 'react-router-dom'; import { connect } from 'react-redux'; -import get from 'lodash/get'; import isEmpty from 'lodash/isEmpty'; import { object, bool, func } from 'prop-types'; import { withStyles } from '@material-ui/core/styles'; @@ -28,26 +27,23 @@ const SearchByOsIdResult = ({ clearProductionLocation, classes, }) => { - const location = useLocation(); const history = useHistory(); + const { osID } = useParams(); useEffect(() => { - const osId = new URLSearchParams(location.search).get('os_id'); - - if (osId) { - fetchProductionLocation(osId); + if (osID) { + fetchProductionLocation(osID); } - }, [location.search, fetchProductionLocation]); + }, [fetchProductionLocation]); - const locationData = get(data, 'data[0]', {}); - const isLocationDataAvailable = !isEmpty(locationData); + const isLocationDataAvailable = !isEmpty(data); const { name, os_id: osId, historical_os_id: historicalOsIds, address, country: { name: countryName } = {}, - } = locationData; + } = data || {}; const handleBackToSearchByNameAddress = () => { clearProductionLocation(); diff --git a/src/react/src/components/Contribute/SearchByOsIdSuccessResult.jsx b/src/react/src/components/Contribute/SearchByOsIdSuccessResult.jsx index d5bd77560..db7c8bccf 100644 --- a/src/react/src/components/Contribute/SearchByOsIdSuccessResult.jsx +++ b/src/react/src/components/Contribute/SearchByOsIdSuccessResult.jsx @@ -2,9 +2,9 @@ import React from 'react'; import { string, object, func, arrayOf } from 'prop-types'; import { withStyles } from '@material-ui/core/styles'; import Typography from '@material-ui/core/Typography'; -import PreviousOsIdTooltip from './PreviousOsIdTooltip'; import SearchByOsIdResultActions from './SearchByOsIdResultActions'; import { makeSearchByOsIdResultStyles } from '../../util/styles'; +import ProductionLocationDetails from './ProductionLocationDetails'; const SearchByOsIdSuccessResult = ({ name, @@ -14,57 +14,26 @@ const SearchByOsIdSuccessResult = ({ countryName, handleBackToSearchByNameAddress, classes, -}) => { - const historicalOsIdsNotEmpty = - Array.isArray(historicalOsIds) && historicalOsIds.length > 0; - - return ( - <> - - Is this your production location? - -
- - {name} - - - {historicalOsIdsNotEmpty ? 'Current OS ID:' : 'OS ID:'}{' '} - {osId} - - {historicalOsIdsNotEmpty && - historicalOsIds.map(historicalOsId => ( - - Previous OS ID: {historicalOsId}{' '} - - - ))} -
- - {address} - - - {countryName} - -
-
- {}} - /> - - ); -}; +}) => ( + <> + + Is this your production location? + + + {}} + /> + +); SearchByOsIdSuccessResult.defaultProps = { historicalOsIds: [], diff --git a/src/react/src/components/Contribute/SearchByOsIdTab.jsx b/src/react/src/components/Contribute/SearchByOsIdTab.jsx index 3cf453f02..44c10298c 100644 --- a/src/react/src/components/Contribute/SearchByOsIdTab.jsx +++ b/src/react/src/components/Contribute/SearchByOsIdTab.jsx @@ -31,9 +31,7 @@ const SearchByOsIdTab = ({ classes }) => { }; const handleSearch = () => { - history.push( - `/contribute/production-location/search/?os_id=${inputOsId}`, - ); + history.push(`/contribute/production-location/search/id/${inputOsId}`); }; return ( diff --git a/src/react/src/reducers/ContributeProductionLocationReducer.js b/src/react/src/reducers/ContributeProductionLocationReducer.js index 3dd1cc4ed..d0c10524d 100644 --- a/src/react/src/reducers/ContributeProductionLocationReducer.js +++ b/src/react/src/reducers/ContributeProductionLocationReducer.js @@ -2,10 +2,13 @@ import { createReducer } from 'redux-act'; import update from 'immutability-helper'; import { - startFetchingSingleProductionLocation, - failFetchingSingleProductionLocation, - completeFetchingSingleProductionLocation, + startFetchSingleProductionLocation, + failFetchSingleProductionLocation, + completeFetchSingleProductionLocation, resetSingleProductionLocation, + startFetchProductionLocations, + failFetchProductionLocations, + completeFetchProductionLocations, } from '../actions/contributeProductionLocation'; const initialState = Object.freeze({ @@ -14,11 +17,16 @@ const initialState = Object.freeze({ fetching: false, error: null, }), + productionLocations: Object.freeze({ + data: null, + fetching: false, + error: null, + }), }); export default createReducer( { - [startFetchingSingleProductionLocation]: state => + [startFetchSingleProductionLocation]: state => update(state, { singleProductionLocation: { fetching: { $set: true }, @@ -26,14 +34,14 @@ export default createReducer( data: { $set: null }, }, }), - [failFetchingSingleProductionLocation]: (state, payload) => + [failFetchSingleProductionLocation]: (state, payload) => update(state, { singleProductionLocation: { fetching: { $set: false }, error: { $set: payload }, }, }), - [completeFetchingSingleProductionLocation]: (state, payload) => + [completeFetchSingleProductionLocation]: (state, payload) => update(state, { singleProductionLocation: { fetching: { $set: false }, @@ -47,6 +55,29 @@ export default createReducer( $set: initialState.singleProductionLocation, }, }), + [startFetchProductionLocations]: state => + update(state, { + productionLocations: { + fetching: { $set: true }, + error: { $set: null }, + data: { $set: null }, + }, + }), + [failFetchProductionLocations]: (state, payload) => + update(state, { + productionLocations: { + fetching: { $set: false }, + error: { $set: payload }, + }, + }), + [completeFetchProductionLocations]: (state, payload) => + update(state, { + productionLocations: { + fetching: { $set: false }, + error: { $set: null }, + data: { $set: payload }, + }, + }), }, initialState, ); diff --git a/src/react/src/util/constants.jsx b/src/react/src/util/constants.jsx index b84170557..44b32aac9 100644 --- a/src/react/src/util/constants.jsx +++ b/src/react/src/util/constants.jsx @@ -350,7 +350,10 @@ export const dashboardClaimsDetailsRoute = '/dashboard/claims/:claimID'; export const aboutClaimedFacilitiesRoute = `${InfoLink}/${InfoPaths.claimedFacilities}`; export const contributeProductionLocationRoute = '/contribute/production-location'; -export const searchByOsIdResultRoute = '/contribute/production-location/search'; +export const searchByOsIdResultRoute = + '/contribute/production-location/search/id/:osID'; +export const searchByNameAndAddressResultRoute = + '/contribute/production-location/search/'; export const contributeFieldsEnum = Object.freeze({ name: 'name',