diff --git a/app/services/stakeholder-best-service.js b/app/services/stakeholder-best-service.js index d56fc8ada..995a703d7 100644 --- a/app/services/stakeholder-best-service.js +++ b/app/services/stakeholder-best-service.js @@ -12,6 +12,9 @@ record has been approved, it will be the most recent version (i.e., have If you make changes to the database structure, be sure to update these methods as well as the corresponding methods in the stakeholder-service.js. +You can search by max/min lat, lng bounds or by a center and radius(distance), +with bounds taking precedence. + */ const booleanEitherClause = (columnName, value) => { @@ -27,13 +30,16 @@ const search = async ({ latitude, longitude, distance, + maxLat, + maxLng, + minLat, + minLng, isInactive, verificationStatusId, tenantId, }) => { const locationClause = buildLocationClause(latitude, longitude); const categoryClause = buildCTEClause(categoryIds, ""); - const sql = `${categoryClause} select s.id, s.name, s.address_1, s.address_2, s.city, s.state, s.zip, s.phone, s.latitude, s.longitude, s.website, s.notes, @@ -69,9 +75,11 @@ const search = async ({ ${buildLoginSelectsClause()} from stakeholder_set as s ${buildLoginJoinsClause()} - where s.tenant_id = ${tenantId} + where s.tenant_id = ${tenantId} ${ - Number(distance) && locationClause + maxLat && maxLng && minLat && minLng + ? buildBounds({ maxLat, maxLng, minLat, minLng }) + : Number(distance) && locationClause ? `AND ${locationClause} < ${distance}` : "" } @@ -83,7 +91,6 @@ const search = async ({ } order by distance `; - // console.log(sql); let stakeholders = []; let categoriesResults = []; var stakeholderResult, stakeholder_ids; @@ -349,6 +356,13 @@ const buildLocationClause = (latitude, longitude) => { return locationClause; }; +const buildBounds = ({ maxLat, maxLng, minLat, minLng }) => { + return ` + AND s.latitude BETWEEN ${minLat} AND ${maxLat} + AND s.longitude BETWEEN ${minLng} AND ${maxLng} + `; +}; + const buildLoginJoinsClause = () => { return ` left join login L1 on s.created_login_id = L1.id diff --git a/client/src/App.js b/client/src/App.js index 7b0e7543d..1fee4acc9 100644 --- a/client/src/App.js +++ b/client/src/App.js @@ -4,7 +4,7 @@ import { makeStyles, ThemeProvider } from "@material-ui/core/styles"; import { Grid } from "@material-ui/core"; import theme from "theme/materialUI"; import { logout } from "services/account-service"; -import { tenantId, originCoordinates } from "helpers/Configuration"; +import { tenantId, defaultCoordinates } from "helpers/Configuration"; // Components import { UserContext } from "components/user-context"; @@ -75,8 +75,8 @@ function App() { const [toast, setToast] = useState({ message: "" }); const [bgImg, setBgImg] = useState(""); const [origin, setOrigin] = useState({ - latitude: originCoordinates.lat, - longitude: originCoordinates.lon, + latitude: defaultCoordinates.lat, + longitude: defaultCoordinates.lon, }); useEffect(() => { @@ -127,8 +127,8 @@ function App() { (error) => { console.log(`Getting browser location failed: ${error.message}`); const userCoordinates = { - latitude: originCoordinates.lat, - longitude: originCoordinates.lon, + latitude: defaultCoordinates.lat, + longitude: defaultCoordinates.lon, }; setUserCoordinates(userCoordinates); } @@ -176,7 +176,8 @@ function App() { diff --git a/client/src/components/Results/ResultsContainer.js b/client/src/components/Results/ResultsContainer.js index 9fc6cfe2c..5b019d7ad 100644 --- a/client/src/components/Results/ResultsContainer.js +++ b/client/src/components/Results/ResultsContainer.js @@ -5,7 +5,7 @@ import { Grid } from "@material-ui/core"; import { useOrganizationBests } from "hooks/useOrganizationBests"; import useCategoryIds from "hooks/useCategoryIds"; import { isMobile } from "helpers"; -import { originCoordinates } from "helpers/Configuration"; +import { defaultCoordinates } from "helpers/Configuration"; import { DEFAULT_CATEGORIES } from "constants/stakeholder"; import Filters from "./ResultsFilters"; @@ -25,11 +25,11 @@ const useStyles = makeStyles((theme) => ({ export default function ResultsContainer({ userCoordinates, - userSearch, + origin, + setOrigin, setToast, }) { // Component state - const storage = window.sessionStorage; const { data, search } = useOrganizationBests(); const [sortedData, setSortedData] = useState([]); const classes = useStyles(); @@ -38,77 +38,34 @@ export default function ResultsContainer({ const [isMapView, setIsMapView] = useState(true); const mobileView = isMobile(); - const doSelectStakeholder = useCallback((stakeholder) => { - if (stakeholder && !mobileView) { - setViewport({ - ...viewport, - latitude: stakeholder.latitude, - longitude: stakeholder.longitude, - }); - } - onSelectStakeholder(stakeholder); + const { categoryIds, toggleCategory } = useCategoryIds([]); + const [isVerifiedSelected, selectVerified] = useState(false); + const [viewport, setViewport] = useState({ + zoom: defaultCoordinates.zoom, + latitude: origin.latitude, + longitude: origin.longitude, + logoPosition: "top-left", }); + const doSelectStakeholder = useCallback( + (stakeholder) => { + if (stakeholder && !isMobile) { + setViewport({ + ...viewport, + latitude: stakeholder.latitude, + longitude: stakeholder.longitude, + }); + } + onSelectStakeholder(stakeholder); + }, + [viewport, setViewport] + ); + const switchResultsView = () => { doSelectStakeholder(); setIsMapView(!isMapView); }; - const initialCategories = storage.categoryIds - ? JSON.parse(storage.categoryIds) - : []; - const { categoryIds, toggleCategory } = useCategoryIds(initialCategories); - - const initialCoords = { - locationName: userSearch - ? userSearch.locationName - : storage.origin - ? JSON.parse(storage.origin).locationName - : "", - latitude: userSearch - ? userSearch.latitude - : storage.origin - ? JSON.parse(storage.origin).latitude - : userCoordinates - ? userCoordinates.latitude - : originCoordinates.lat, - longitude: userSearch - ? userSearch.longitude - : storage.origin - ? JSON.parse(storage.origin).longitude - : userCoordinates - ? userCoordinates.longitude - : originCoordinates.lon, - }; - - const [radius, setRadius] = useState( - storage?.radius ? JSON.parse(storage.radius) : 5 - ); - const [origin, setOrigin] = useState(initialCoords); - const [isVerifiedSelected, selectVerified] = useState( - storage?.verified ? JSON.parse(storage.verified) : false - ); - - const viewPortHash = { - 0: 4, - 1: 13.5, - 2: 12.5, - 3: 12, - 5: 11, - 10: 10, - 20: 9, - 50: 8, - 100: 7, - 500: 4.5, - }; - - const [viewport, setViewport] = useState({ - zoom: viewPortHash[radius || 0], - latitude: origin.latitude || JSON.parse(storage.origin).latitude, - longitude: origin.longitude || JSON.parse(storage.origin).longitude, - logoPosition: "top-left", - }); - // Component effects useEffect(() => { @@ -135,44 +92,26 @@ export default function ResultsContainer({ setSortedData(data.sort(sortOrganizations)); }, [data]); - useEffect(() => { - return () => { - sessionStorage.clear(); - }; - }, []); - const handleSearch = useCallback( - (e, center) => { + (e, center, bounds) => { if (e) e.preventDefault(); search({ latitude: - (center && center.lat) || - origin.latitude || - userCoordinates.latitude || - JSON.parse(storage.origin).latitude, + (center && center.lat) || origin.latitude || userCoordinates.latitude, longitude: (center && center.lng) || origin.longitude || - userCoordinates.longitude || - JSON.parse(storage.origin).longitude, - radius, + userCoordinates.longitude, categoryIds: categoryIds.length ? categoryIds : DEFAULT_CATEGORIES, isInactive: "either", verificationStatusId: 0, + bounds, + radius: defaultCoordinates.radius, }); - if (origin.locationName && origin.latitude && origin.longitude) - storage.origin = JSON.stringify({ - locationName: origin.locationName, - latitude: origin.latitude, - longitude: origin.longitude, - }); - storage.categoryIds = JSON.stringify(categoryIds); - storage.radius = JSON.stringify(radius); - storage.verified = JSON.stringify(isVerifiedSelected); if (!center) setViewport({ - zoom: viewPortHash[radius || 0], + zoom: defaultCoordinates.zoom, latitude: origin.latitude, longitude: origin.longitude, }); @@ -180,29 +119,19 @@ export default function ResultsContainer({ }, [ search, - origin.locationName, origin.latitude, origin.longitude, userCoordinates.latitude, userCoordinates.longitude, - radius, categoryIds, - isVerifiedSelected, setViewport, doSelectStakeholder, - viewPortHash, - storage.categoryIds, - storage.radius, - storage.verified, - storage.origin, ] ); return ( <> @@ -229,8 +155,6 @@ export default function ResultsContainer({ {(!mobileView || (mobileView && isMapView)) && ( )} diff --git a/client/src/components/Results/ResultsFilters.js b/client/src/components/Results/ResultsFilters.js index 48eedbacc..70bf27c93 100644 --- a/client/src/components/Results/ResultsFilters.js +++ b/client/src/components/Results/ResultsFilters.js @@ -1,8 +1,9 @@ import React, { useCallback, useEffect } from "react"; import PropTypes from "prop-types"; import { makeStyles } from "@material-ui/core/styles"; -import { Grid, Select, MenuItem, Button, Box } from "@material-ui/core"; +import { Grid, Button, Box } from "@material-ui/core"; import SearchIcon from "@material-ui/icons/Search"; +import LocationSearchingIcon from "@material-ui/icons/LocationSearching"; import { MEAL_PROGRAM_CATEGORY_ID, @@ -29,30 +30,54 @@ const useStyles = makeStyles((theme) => ({ position: "sticky", top: "48px", zIndex: 1, - [theme.breakpoints.up("md")]: { - justifyContent: "center", - }, + justifyContent: "center", }, inputContainer: { display: "flex", alignItems: "center", width: "100%", - [theme.breakpoints.up("md")]: { - width: "30rem", - }, + maxWidth: "30rem", + margin: "0 0.5rem", + }, + form: { + all: "inherit", + backgroundColor: "white", + borderRadius: "6px", }, searchIcon: { width: 32, height: 32, }, - submit: { + nearbyIcon: { + maxWidth: "30px", + }, + nearbySearch: { height: "40px", minWidth: "25px", + padding: 0, + marginLeft: "5px", + borderRadius: 0, + backgroundColor: "white", + boxShadow: "none", + "& .MuiButton-startIcon": { + margin: 0, + }, + "&.Mui-disabled": { + opacity: 0.8, + backgroundColor: "white", + }, + "&:hover": { + boxShadow: "none", + }, + }, + submit: { + height: "40px", backgroundColor: "#BCE76D", borderRadius: "0 6px 6px 0", boxShadow: "none", "& .MuiButton-startIcon": { marginRight: 0, + marginLeft: "3px", }, "&.Mui-disabled": { backgroundColor: "#BCE76D", @@ -62,9 +87,6 @@ const useStyles = makeStyles((theme) => ({ backgroundColor: "#C7F573", boxShadow: "none", }, - [theme.breakpoints.down("sm")]: { - marginRight: ".5rem", - }, }, buttonHolder: { display: "flex", @@ -74,21 +96,14 @@ const useStyles = makeStyles((theme) => ({ }, })); -const distanceInfo = [0, 1, 2, 3, 5, 10, 20, 50, 100, 500]; - const ResultsFilters = ({ handleSearch, - viewport, - setViewport, origin, setOrigin, - radius, - setRadius, isVerifiedSelected, userCoordinates, categoryIds, toggleCategory, - viewPortHash, isMapView, switchResultsView, }) => { @@ -107,15 +122,7 @@ const ResultsFilters = ({ useEffect(() => { handleSearch(); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [origin, radius, categoryIds, isVerifiedSelected, toggleCategory]); - - const handleDistanceChange = (distance) => { - setRadius(distance); - setViewport({ - ...viewport, - zoom: viewPortHash[distance], - }); - }; + }, [origin, categoryIds, isVerifiedSelected, toggleCategory]); const mobileView = isMobile(); @@ -131,34 +138,6 @@ const ResultsFilters = ({ alignItems="center" className={classes.buttonHolder} > - - -