@@ -285,36 +283,21 @@ const StakeholderDetails = ({
: selectedStakeholder.createdDate.format("MMM Do, YYYY")}
) : null}
-
-
-
- Directions
-
-
+
+
+ window.open(
+ getGoogleMapsUrl(
+ selectedStakeholder.zip,
+ selectedStakeholder.address1,
+ selectedStakeholder.address2 || null
+ )
+ )
+ }
+ />
Phone
- {selectedStakeholder.phone ? (
-
-
- {selectedStakeholder.phone}
-
-
+ {numbers.length ? (
+ {numbers}
) : (
No Phone Number on record
)}
diff --git a/client/src/components/Stakeholder/StakeholderList.js b/client/src/components/Stakeholder/StakeholderList.js
deleted file mode 100644
index 8e24ef641..000000000
--- a/client/src/components/Stakeholder/StakeholderList.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import React from "react";
-import { withRouter } from "react-router-dom";
-import { Grid, Typography } from "@material-ui/core";
-import StakeholderListItem from "./StakeholderListItem";
-
-const StakeholderList = (props) => {
- const { stakeholders } = props;
- return (
-
- {stakeholders &&
- stakeholders.length > 0 &&
- stakeholders.map((stakeholder) => (
-
- ))}
- {!stakeholders ||
- (stakeholders.length < 1 && (
-
-
- No matches found, please try different Criteria
-
-
- ))}
- )}
-
- );
-};
-
-export default withRouter(StakeholderList);
diff --git a/client/src/components/Stakeholder/StakeholderListItem.js b/client/src/components/Stakeholder/StakeholderListItem.js
deleted file mode 100644
index 924f15add..000000000
--- a/client/src/components/Stakeholder/StakeholderListItem.js
+++ /dev/null
@@ -1,80 +0,0 @@
-import React from "react";
-import { Grid, Typography, Card, CardContent, Link } from "@material-ui/core";
-import { Check } from "@material-ui/icons";
-import { UserContext } from "components/user-context";
-import { EditButton } from "components/Buttons";
-import { withRouter } from "react-router-dom";
-import moment from "moment";
-
-const StakeholderListItem = (props) => {
- const { stakeholder } = props;
- return (
-
-
-
-
- {stakeholder.name}
-
-
- {stakeholder.categories.map((cat) => cat.name).join(", ")}
-
-
- {stakeholder.website ? (
-
-
- {stakeholder.website}
-
-
- ) : null}
- {stakeholder.address1}
- {stakeholder.address2 ? {stakeholder.address2}
: null}
-
- {stakeholder.city}, {stakeholder.state} {stakeholder.zip}
-
- {/*
- longitude: {stakeholder.longitude} latitude: {stakeholder.latitude}
-
*/}
- {stakeholder.distance && (
- distance: {stakeholder.distance.toFixed(2)} mi.
- )}
-
- {stakeholder.verifiedDate ? (
-
-
-
-
-
-
- {" " + moment(stakeholder.verifiedDate).format("MM/DD/YY")}
-
-
-
- ) : (
-
- )}
-
- {(user) =>
- user && (user.isAdmin || user.isCoordinator) ? (
-
-
-
- ) : null
- }
-
-
-
-
-
- );
-};
-
-export default withRouter(StakeholderListItem);
diff --git a/client/src/components/Stakeholder/StakeholderPreview.js b/client/src/components/Stakeholder/StakeholderPreview.js
index 6ea87d1b2..9565bd4af 100644
--- a/client/src/components/Stakeholder/StakeholderPreview.js
+++ b/client/src/components/Stakeholder/StakeholderPreview.js
@@ -2,13 +2,16 @@ import React from "react";
import PropTypes from "prop-types";
import moment from "moment";
import { makeStyles } from "@material-ui/core/styles";
+
import {
MEAL_PROGRAM_CATEGORY_ID,
FOOD_PANTRY_CATEGORY_ID,
} from "constants/stakeholder";
-
import { ORGANIZATION_COLORS, CLOSED_COLOR } from "constants/map";
-import getIcon from "helpers/getIcon";
+import { getGoogleMapsUrl, extractNumbers } from "helpers";
+
+import Icon from "components/Icon";
+import { PlainButton } from "components/Buttons";
const useStyles = makeStyles(() => ({
stakeholder: {
@@ -19,31 +22,20 @@ const useStyles = makeStyles(() => ({
alignItems: "center",
padding: "1em 0",
},
- img: {
- display: "flex",
- marginRight: "1em",
- width: "50px",
- },
info: {
fontSize: "1em",
textAlign: "left",
width: "100%",
+ display: "flex",
flexDirection: "column",
justifyContent: "space-between",
"& p": {
margin: 0,
},
},
- check: {
- width: "10%",
- display: "flex",
- flexDirection: "column",
- justifyContent: "space-between",
- },
label: {
width: "100%",
display: "flex",
- flexDirection: "column",
},
closedLabel: {
color: "#545454",
@@ -51,6 +43,7 @@ const useStyles = makeStyles(() => ({
backgroundColor: "#E0E0E0",
padding: ".25em .5em",
borderRadius: "3px",
+ margin: ".25em 0",
},
openLabel: {
color: "#fff",
@@ -59,6 +52,7 @@ const useStyles = makeStyles(() => ({
padding: ".25em",
borderRadius: "3px",
margin: ".25em 0",
+ marginRight: "0.25em",
},
closingSoonLabel: {
color: "#fff",
@@ -68,6 +62,22 @@ const useStyles = makeStyles(() => ({
borderRadius: "3px",
margin: ".25em 0",
},
+ buttons: {
+ width: "100%",
+ display: "flex",
+ flexDirection: "row",
+ justifyContent: "space-between",
+ },
+ leftInfo: {
+ display: "flex",
+ flexDirection: "column",
+ justifyContent: "space-between",
+ alignItems: "center",
+ marginRight: "1em",
+ },
+ name: {
+ fontSize: "16px",
+ },
}));
const isLastOccurrenceInMonth = (currentDay) => {
@@ -128,6 +138,8 @@ const isAlmostClosed = (hours) => {
const StakeholderPreview = ({ stakeholder, doSelectStakeholder }) => {
const classes = useStyles();
+ const mainNumber = extractNumbers(stakeholder.phone).find((n) => n.number);
+
const stakeholderHours = stakeholdersCurrentDaysHours(stakeholder);
const isOpenFlag = !!stakeholderHours;
const isAlmostClosedFlag = isOpenFlag && isAlmostClosed(stakeholderHours);
@@ -140,31 +152,39 @@ const StakeholderPreview = ({ stakeholder, doSelectStakeholder }) => {
key={stakeholder.id}
onClick={() => doSelectStakeholder(stakeholder)}
>
- {getIcon(stakeholder)}
+
+
+
+ {stakeholder.distance >= 10
+ ? stakeholder.distance.toString().substring(0, 3).padEnd(4, "0")
+ : stakeholder.distance.toString().substring(0, 3)}{" "}
+ mi
+
+
-
{stakeholder.name}
-
{stakeholder.address1}
-
- {stakeholder.city} {stakeholder.zip}
-
- {stakeholder.categories.map((category) => (
-
- {category.name}
-
- ))}
+
{stakeholder.name}
+
+ {stakeholder.categories.map((category) => (
+
+ {category.name}
+
+ ))}
+
{stakeholder.inactiveTemporary || stakeholder.inactive ? (
@@ -187,12 +207,29 @@ const StakeholderPreview = ({ stakeholder, doSelectStakeholder }) => {
) : null}
-
-
- {stakeholder.distance >= 10
- ? stakeholder.distance.toString().substring(0, 3).padEnd(4, "0")
- : stakeholder.distance.toString().substring(0, 3)}{" "}
- mi
+
+
+ window.open(
+ getGoogleMapsUrl(
+ stakeholder.zip,
+ stakeholder.address1,
+ stakeholder.address2 || null
+ )
+ )
+ }
+ size="small"
+ />
+ {mainNumber && (
+ window.open(`tel:${mainNumber.value}`)}
+ />
+ )}
+
+
);
diff --git a/client/src/components/Stakeholder/StakeholderSearch.js b/client/src/components/Stakeholder/StakeholderSearch.js
deleted file mode 100644
index 31ab23a99..000000000
--- a/client/src/components/Stakeholder/StakeholderSearch.js
+++ /dev/null
@@ -1,299 +0,0 @@
-import React, { useState } from "react";
-import { makeStyles } from "@material-ui/core/styles";
-import {
- Card,
- CardContent,
- Checkbox,
- Input,
- ListItemText,
- MenuItem,
- Select,
- Grid,
- TextField,
- Chip,
- FormLabel,
- FormControlLabel,
- Typography,
- RadioGroup,
- Radio,
-} from "@material-ui/core";
-import { SearchButton } from "components/Buttons";
-import SwitchViewsButton from "components/SwitchViewsButton";
-import LocationAutocomplete from "components/LocationAutocomplete";
-
-const useStyles = makeStyles((theme) => ({
- card: {
- margin: "0px",
- },
- chips: {
- display: "flex",
- flexWrap: "wrap",
- },
- chip: {
- margin: 2,
- },
- formLabel: {
- margin: "1rem 0 .5rem",
- },
-}));
-
-const closeTo = (lat1, lon1, lat2, lon2) => {
- return Math.abs(lat1 - lat2) + Math.abs(lon1 - lon2) < 0.01;
-};
-
-function StakeholderSearch(props) {
- const [selectedCategories, setSelectedCategories] = useState(
- props.selectedCategories
- );
- const [searchString, setSearchString] = useState(props.searchString);
- const [latitude] = useState(props.latitude);
- const [longitude] = useState(props.longitude);
- const [customLatitude, setCustomLatitude] = useState(props.selectedLatitude);
- const [customLongitude, setCustomLongitude] = useState(
- props.selectedLongitude
- );
- const [customLocationName, setCustomLocationName] = useState(
- props.selectedLocationName
- );
-
- const [selectedLatitude, setSelectedLatitude] = useState(
- props.selectedLatitude
- );
- const [selectedLongitude, setSelectedLongitude] = useState(
- props.selectedLongitude
- );
- const [selectedLocationName, setSelectedLocationName] = useState(
- props.selectedLocationName
- );
- const [selectedDistance, setSelectedDistance] = useState(
- props.selectedDistance
- );
- const [useMyLocation, setUseMyLocation] = useState(
- latitude
- ? closeTo(
- props.selectedLatitude,
- props.selectedLongitude,
- props.latitude,
- props.longitude
- )
- ? "my"
- : "custom"
- : "custom"
- );
-
- const classes = useStyles();
-
- const handleRadioChange = (evt) => {
- const val = evt.target.value;
- setUseMyLocation(val);
- if (val === "my") {
- setSelectedLatitude(latitude);
- setSelectedLongitude(longitude);
- setSelectedLocationName("");
- } else {
- setSelectedLatitude(customLatitude);
- setSelectedLongitude(customLongitude);
- setSelectedLocationName(customLocationName);
- }
- };
-
- const setLocation = (location) => {
- setCustomLatitude(location.location.y);
- setCustomLongitude(location.location.x);
- setCustomLocationName(location.address);
- setSelectedLatitude(location.location.y);
- setSelectedLongitude(location.location.x);
- setSelectedLocationName(location.address);
- setUseMyLocation("custom");
- };
-
- return (
-
-
-
-
-
- {
- props.search(
- searchString,
- selectedLatitude,
- selectedLongitude,
- selectedLocationName,
- selectedCategories,
- selectedDistance
- );
- }}
- />
-
-
-
-
-
-
-
- Name
- {
- setSearchString(event.target.value);
- }}
- />
-
-
- Categories
-
-
-
-
- Location
-
- {"Within "}
-
- {"miles of"}
-
-
- {/* If we got location from browser, allow using current location */}
- {latitude ? (
-
- }
- style={{ alignItems: "flex-start" }}
- label={
-
- My Location:
- {`(${latitude}, ${longitude})`}
-
- }
- />
- }
- style={{ alignItems: "flex-start" }}
- label={
-
-
- {`Custom Location:`}
- {customLocationName ? (
- {customLocationName}
- ) : null}
- {customLatitude ? (
- {`(${customLatitude.toFixed(
- 6
- )}, ${customLongitude.toFixed(6)})`}
- ) : null}
-
-
-
- }
- />
-
- ) : (
-
- {customLocationName ? (
- {customLocationName}
- ) : null}
- {customLatitude ? (
- {`(${customLatitude.toFixed(
- 6
- )}, ${customLongitude.toFixed(6)})`}
- ) : null}
-
-
-
- )}
-
-
-
-
-
- );
-}
-
-export default StakeholderSearch;
diff --git a/client/src/components/Stakeholder/StakeholdersContainer.js b/client/src/components/Stakeholder/StakeholdersContainer.js
deleted file mode 100644
index c91eaafc1..000000000
--- a/client/src/components/Stakeholder/StakeholdersContainer.js
+++ /dev/null
@@ -1,124 +0,0 @@
-import React from "react";
-import { withRouter } from "react-router-dom";
-import { Typography } from "@material-ui/core";
-import StakeholderSearch from "./StakeholderSearch";
-import StakeholderCriteria from "./StakeholderCriteria";
-import StakeholderList from "./StakeholderList";
-import Map from "components/Map";
-import { RotateLoader } from "react-spinners";
-import { useStakeholders } from "hooks/useStakeholders/useStakeholders";
-
-const styles = {
- container: {
- display: "flex",
- flexDirection: "column",
- padding: "1rem",
- },
- header: {
- display: "flex",
- },
-};
-
-function StakeholdersContainer(props) {
- const { history, userCoordinates } = props;
- const { state, dispatch, actionTypes, search } = useStakeholders(history);
- const [isMapView, setIsMapView] = React.useState(true);
- const openSearchPanel = (isOpen) => {
- dispatch({ type: actionTypes.TOGGLE_SEARCH_PANEL, isOpen });
- };
-
- const {
- stakeholders,
- categories,
- searchString,
- selectedLongitude,
- selectedLatitude,
- selectedLocationName,
- selectedDistance,
- selectedCategories,
- isSearchPanelOpen,
- isLoading,
- latitude,
- longitude,
- } = state;
-
- return (
-
-
-
- Stakeholders{" "}
-
-
- <>
- {isSearchPanelOpen ? (
- setIsMapView(!isMapView)}
- isMapView={isMapView}
- />
- ) : (
- setIsMapView(!isMapView)}
- isMapView={isMapView}
- />
- )}
- {isSearchPanelOpen ? null : isLoading ? (
-
-
-
- ) : isMapView && selectedLatitude && selectedLongitude ? (
-
- ) : (
-
- )}
- >
-
- );
-}
-
-export default withRouter(StakeholdersContainer);
diff --git a/client/src/components/Stakeholder/StakeholderEdit.js b/client/src/components/Verification/OrganizationEdit.js
similarity index 99%
rename from client/src/components/Stakeholder/StakeholderEdit.js
rename to client/src/components/Verification/OrganizationEdit.js
index d2cf2b04a..d0444025f 100644
--- a/client/src/components/Stakeholder/StakeholderEdit.js
+++ b/client/src/components/Verification/OrganizationEdit.js
@@ -129,7 +129,7 @@ const validationSchema = Yup.object().shape({
),
});
-const emptyStakeholder = {
+const emptyOrganization = {
id: 0,
name: "",
description: "",
@@ -194,7 +194,7 @@ const emptyStakeholder = {
inactiveTemporary: false,
};
-const StakeholderEdit = (props) => {
+const OrganizationEdit = (props) => {
const { classes, setToast, match, user, history } = props;
const editId = match.params.id;
const [assignDialogOpen, setAssignDialogOpen] = useState(false);
@@ -204,7 +204,7 @@ const StakeholderEdit = (props) => {
const [tabPage, setTabPage] = useState(0);
const [geocodeResults, setGeocodeResults] = useState([]);
const [nextUrl, setNextUrl] = useState(null);
- const [originalData, setOriginalData] = useState(emptyStakeholder);
+ const [originalData, setOriginalData] = useState(emptyOrganization);
const { data: categories } = useCategories();
@@ -223,7 +223,7 @@ const StakeholderEdit = (props) => {
setOriginalData(stakeholder);
} else {
- setOriginalData(emptyStakeholder);
+ setOriginalData(emptyOrganization);
}
} catch (err) {
console.log(err);
@@ -2017,7 +2017,7 @@ const StakeholderEdit = (props) => {
);
};
-StakeholderEdit.propTypes = {
+OrganizationEdit.propTypes = {
classes: PropTypes.object,
setToast: PropTypes.func,
match: PropTypes.object,
@@ -2025,4 +2025,4 @@ StakeholderEdit.propTypes = {
history: PropTypes.object,
};
-export default withStyles(styles)(withRouter(StakeholderEdit));
+export default withStyles(styles)(withRouter(OrganizationEdit));
diff --git a/client/src/components/Verification/VerificationAdmin.js b/client/src/components/Verification/VerificationAdmin.js
index 2599736a0..a8e430749 100644
--- a/client/src/components/Verification/VerificationAdmin.js
+++ b/client/src/components/Verification/VerificationAdmin.js
@@ -8,7 +8,7 @@ import { SearchButton } from "../Buttons";
import { PrimaryButton } from "../../ui";
import StakeholderGrid from "./VerificationAdminGrid";
import { RotateLoader } from "react-spinners";
-import { useOrganizations } from "hooks/useOrganizations/useOrganizations";
+import { useOrganizations } from "hooks/useOrganizations";
import { useCategories } from "hooks/useCategories/useCategories";
import { useTenants } from "hooks/useTenants/useTenants";
import { useNeighborhoods } from "hooks/useNeighborhoods/useNeighborhoods";
@@ -153,7 +153,7 @@ function VerificationAdmin(props) {
data: stakeholders,
loading: stakeholdersLoading,
error: stakeholdersError,
- searchDashboard: stakeholderSearch,
+ search: stakeholderSearch,
} = useOrganizations();
const searchCallback = useCallback(stakeholderSearch, []);
diff --git a/client/src/components/Verification/VerificationDashboard.js b/client/src/components/Verification/VerificationDashboard.js
index 9f42dc561..9ba3b15da 100644
--- a/client/src/components/Verification/VerificationDashboard.js
+++ b/client/src/components/Verification/VerificationDashboard.js
@@ -5,7 +5,7 @@ import { makeStyles } from "@material-ui/core/styles";
import { SearchButton, PlainButton } from "../Buttons";
import StakeholderGrid from "./VerificationAdminGrid";
import { RotateLoader } from "react-spinners";
-import { useOrganizations } from "hooks/useOrganizations/useOrganizations";
+import { useOrganizations } from "hooks/useOrganizations";
import * as stakeholderService from "services/stakeholder-service";
const useStyles = makeStyles((theme) => ({
@@ -80,7 +80,7 @@ function VerificationDashboard(props) {
data: stakeholders,
loading: stakeholdersLoading,
error: stakeholdersError,
- searchDashboard: stakeholderSearch,
+ search: stakeholderSearch,
} = useOrganizations();
const searchCallback = useCallback(stakeholderSearch, []);
diff --git a/client/src/containers/Home/index.js b/client/src/containers/Home/index.js
index b8425ee99..9d41b0099 100644
--- a/client/src/containers/Home/index.js
+++ b/client/src/containers/Home/index.js
@@ -11,7 +11,7 @@ import Box from "@material-ui/core/Box";
import Search from "components/Search";
import logo from "images/foodoasisla.svg";
import logoCA from "images/foodoasisca.svg";
-import { getTenantId } from "../../helpers/Configuration";
+import { getTenantId } from "helpers/Configuration";
const useStyles = makeStyles((theme) => ({
container: {
diff --git a/client/src/helpers/getIcon.js b/client/src/helpers/getIcon.js
deleted file mode 100644
index 24782cd9f..000000000
--- a/client/src/helpers/getIcon.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import pantryIcon from "../images/pantryIcon";
-import mealIcon from "../images/mealIcon";
-import splitPantryMealIcon from "../images/splitPantryMealIcon";
-import {
- MEAL_PROGRAM_CATEGORY_ID,
- FOOD_PANTRY_CATEGORY_ID,
-} from "../constants/stakeholder";
-
-const getIcon = (stakeholder) => {
- let isClosed = false;
- if (stakeholder.inactiveTemporary || stakeholder.inactive) isClosed = true;
-
- return stakeholder.categories.some(
- (category) => category.id === FOOD_PANTRY_CATEGORY_ID
- ) &&
- stakeholder.categories.some(
- (category) => category.id === MEAL_PROGRAM_CATEGORY_ID
- )
- ? splitPantryMealIcon(isClosed)
- : stakeholder.categories[0].id === FOOD_PANTRY_CATEGORY_ID
- ? pantryIcon(isClosed)
- : mealIcon(isClosed);
-};
-
-export default getIcon;
diff --git a/client/src/helpers/index.js b/client/src/helpers/index.js
new file mode 100644
index 000000000..17e665da7
--- /dev/null
+++ b/client/src/helpers/index.js
@@ -0,0 +1,33 @@
+export const getGoogleMapsUrl = (zip, address1, address2) => {
+ const baseUrl = `https://google.com/maps/place/`;
+
+ const address1urlArray = address1.split(" ");
+ const address1url = address1urlArray.reduce(
+ (acc, currentWord) => `${acc}+${currentWord}`
+ );
+
+ if (address2) {
+ const address2urlArray = address2.split(" ");
+ const address2url = address2urlArray.reduce(
+ (acc, currentWord) => `${acc}+${currentWord}`
+ );
+ return `${baseUrl}${address1url},+${address2url},+${zip}`;
+ }
+
+ return `${baseUrl}${address1url},+${zip}`;
+};
+
+export const isMobile = new RegExp("Mobi", "i").test(navigator.userAgent)
+ ? true
+ : false;
+
+export const extractNumbers = (numbers) =>
+ numbers.split(/(and)|,|&+/).map((n) => {
+ const match = new RegExp(
+ "\\+?\\(?\\d*\\)? ?\\(?\\d+\\)?\\d*([\\s./-]?\\d{2,})+",
+ "g"
+ ).exec(n);
+ return match
+ ? { number: true, value: match[0] }
+ : { number: false, value: n };
+ });
diff --git a/client/src/helpers/isMobile.js b/client/src/helpers/isMobile.js
deleted file mode 100644
index 13526e87e..000000000
--- a/client/src/helpers/isMobile.js
+++ /dev/null
@@ -1,5 +0,0 @@
-const isMobile = new RegExp("Mobi", "i").test(navigator.userAgent)
- ? true
- : false;
-
-export default isMobile;
diff --git a/client/src/hooks/useMapboxGeocoder.js b/client/src/hooks/useMapboxGeocoder.js
index e124363e8..0cd853db0 100644
--- a/client/src/hooks/useMapboxGeocoder.js
+++ b/client/src/hooks/useMapboxGeocoder.js
@@ -1,16 +1,13 @@
import React from "react";
import axios from "axios";
import debounce from "debounce-fn";
-import { getTenantId } from "../helpers/Configuration";
+import { getTenantId } from "helpers/Configuration";
const baseUrl = `https://api.mapbox.com/geocoding/v5/mapbox.places`;
const losAngelesCountyLatLong = "-118.9517,33.6988,-117.6462,34.8233";
const californiaLatLong = "-124.389, 32.4796, -114.1723, 42.072";
-const MAPBOX_TOKEN =
- "pk.eyJ1IjoibHVjYXNob21lciIsImEiOiJjazFqcnRjcm0wNmZ1M2JwZXg2eDFzMXd3In0.yYpkKLrFCxF-qyBfZH1a8w";
-
const initialState = {
isLoading: false,
error: false,
@@ -52,7 +49,7 @@ export function useMapboxGeocoder() {
async (searchString) => {
const bbox =
getTenantId() === 1 ? losAngelesCountyLatLong : californiaLatLong;
- const mapboxUrl = `${baseUrl}/${searchString}.json?bbox=${bbox}&access_token=${MAPBOX_TOKEN}`;
+ const mapboxUrl = `${baseUrl}/${searchString}.json?bbox=${bbox}&access_token=${process.env.REACT_APP_MAPBOX_TOKEN}`;
dispatch({ type: actionTypes.FETCH_REQUEST });
try {
diff --git a/client/src/hooks/useOrganizationBests.js b/client/src/hooks/useOrganizationBests.js
new file mode 100644
index 000000000..d4d8ed82a
--- /dev/null
+++ b/client/src/hooks/useOrganizationBests.js
@@ -0,0 +1,49 @@
+import { useState } from "react";
+import * as stakeholderService from "../services/stakeholder-best-service";
+
+export const useOrganizationBests = () => {
+ const [state, setState] = useState({
+ data: null,
+ loading: false,
+ error: false,
+ });
+
+ const search = async ({
+ name,
+ latitude,
+ longitude,
+ radius,
+ categoryIds,
+ isInactive,
+ verificationStatusId,
+ }) => {
+ if (!latitude || !longitude) {
+ setState({ data: null, loading: false, error: true });
+ const msg =
+ "Call to search function missing latitude and/or longitude parameters";
+ console.error(msg);
+ return Promise.reject(msg);
+ }
+ //if (!categoryIds || categoryIds.length === 0) return;
+ try {
+ setState({ data: null, loading: true, error: false });
+ const stakeholders = await stakeholderService.search({
+ name,
+ categoryIds,
+ latitude,
+ longitude,
+ distance: radius,
+ isInactive,
+ verificationStatusId,
+ });
+ setState({ data: stakeholders, loading: false, error: false });
+ return stakeholders;
+ } catch (err) {
+ setState({ data: null, loading: false, error: true });
+ console.error(err);
+ return Promise.reject(err);
+ }
+ };
+
+ return { ...state, search };
+};
diff --git a/client/src/hooks/useOrganizations/useOrganizations.js b/client/src/hooks/useOrganizations.js
similarity index 55%
rename from client/src/hooks/useOrganizations/useOrganizations.js
rename to client/src/hooks/useOrganizations.js
index c188b14f1..97333acc9 100644
--- a/client/src/hooks/useOrganizations/useOrganizations.js
+++ b/client/src/hooks/useOrganizations.js
@@ -1,5 +1,5 @@
import { useState } from "react";
-import * as stakeholderService from "../../services/stakeholder-service";
+import * as stakeholderService from "../services/stakeholder-service";
export const useOrganizations = () => {
const [state, setState] = useState({
@@ -9,43 +9,6 @@ export const useOrganizations = () => {
});
const search = async ({
- name,
- latitude,
- longitude,
- radius,
- categoryIds,
- isInactive,
- verificationStatusId,
- }) => {
- if (!latitude || !longitude) {
- setState({ data: null, loading: false, error: true });
- const msg =
- "Call to search function missing latitude and/or longitude parameters";
- console.error(msg);
- return Promise.reject(msg);
- }
- //if (!categoryIds || categoryIds.length === 0) return;
- try {
- setState({ data: null, loading: true, error: false });
- const stakeholders = await stakeholderService.search({
- name,
- categoryIds,
- latitude,
- longitude,
- distance: radius,
- isInactive,
- verificationStatusId,
- });
- setState({ data: stakeholders, loading: false, error: false });
- return stakeholders;
- } catch (err) {
- setState({ data: null, loading: false, error: true });
- console.error(err);
- return Promise.reject(err);
- }
- };
-
- const searchDashboard = async ({
tenantId,
name,
latitude,
@@ -68,7 +31,7 @@ export const useOrganizations = () => {
}) => {
try {
setState({ data: null, loading: true, error: false });
- const stakeholders = await stakeholderService.searchDashboard({
+ const stakeholders = await stakeholderService.search({
tenantId,
name,
latitude,
@@ -98,5 +61,5 @@ export const useOrganizations = () => {
}
};
- return { ...state, search, searchDashboard };
+ return { ...state, search };
};
diff --git a/client/src/hooks/useStakeholders/actionTypes.js b/client/src/hooks/useStakeholders/actionTypes.js
deleted file mode 100644
index 0adb2a025..000000000
--- a/client/src/hooks/useStakeholders/actionTypes.js
+++ /dev/null
@@ -1,20 +0,0 @@
-export const actionTypes = {
- STAKEHOLDERS: {
- FETCH_REQUEST: "STAKEHOLDERS_FETCH_REQUEST",
- FETCH_SUCCESS: "STAKEHOLDERS_FETCH_SUCCESS",
- FETCH_FAILURE: "STAKEHOLDERS_FETCH_FAILURE",
- },
- CATEGORIES: {
- FETCH_REQUEST: "CATEGORIES_FETCH_REQUEST",
- FETCH_SUCCESS: "CATEGORIES_FETCH_SUCCESS",
- FETCH_FAILURE: "CATEGORIES_FETCH_FAILURE",
- },
- LOCATION: {
- FETCH_REQUEST: "LOCATION_FETCH_REQUEST",
- FETCH_SUCCESS: "LOCATION_FETCH_SUCCESS",
- FETCH_FAILURE: "LOCATION_FETCH_FAILURE",
- },
- INITIALIZE_STATE: "INITIALIZE_STATE",
- UPDATE_CRITERIA: "UPDATE_CRITERIA",
- TOGGLE_SEARCH_PANEL: "TOGGLE_SEARCH_PANEL",
-};
diff --git a/client/src/hooks/useStakeholders/initialState.js b/client/src/hooks/useStakeholders/initialState.js
deleted file mode 100644
index a2f168554..000000000
--- a/client/src/hooks/useStakeholders/initialState.js
+++ /dev/null
@@ -1,19 +0,0 @@
-export const initialState = {
- isLoading: false,
- categoriesError: null,
- stakeholdersError: null,
- locationError: null,
- isSearchPanelOpen: false,
- stakeholders: [],
- categories: [],
- searchString: "",
- selectedCategoryIds: [1, 8, 9],
- selectedCategories: null,
- latitude: null,
- longitude: null,
- selectedLatitude: 34.041001,
- selectedLongitude: -118.235036,
- selectedLocationName: "",
- selectedDistance: 10,
- queryParametersLoaded: false,
-};
diff --git a/client/src/hooks/useStakeholders/reducer.js b/client/src/hooks/useStakeholders/reducer.js
deleted file mode 100644
index 86352045f..000000000
--- a/client/src/hooks/useStakeholders/reducer.js
+++ /dev/null
@@ -1,61 +0,0 @@
-import { actionTypes } from "./actionTypes";
-
-export function reducer(state, action) {
- switch (action.type) {
- case actionTypes.STAKEHOLDERS.FETCH_REQUEST:
- return { ...state, isLoading: true };
- case actionTypes.STAKEHOLDERS.FETCH_SUCCESS:
- return {
- ...state,
- stakeholders: action.stakeholders,
- ...action.payload,
- isLoading: false,
- isSearchPanelOpen: false,
- };
- case actionTypes.STAKEHOLDERS.FETCH_FAILURE:
- return { ...state, stakeholdersError: action.error, isLoading: false };
-
- case actionTypes.CATEGORIES.FETCH_REQUEST:
- return { ...state, isLoading: true };
- case actionTypes.CATEGORIES.FETCH_SUCCESS:
- return {
- ...state,
- categories: action.categories,
- selectedCategories: action.selectedCategories,
- isLoading: false,
- };
- case actionTypes.CATEGORIES.FETCH_FAILURE:
- return {
- ...state,
- categoriesError: action.error,
- //isLoading: false
- };
-
- case actionTypes.LOCATION.FETCH_REQUEST:
- return { ...state, isLoading: true };
- case actionTypes.LOCATION.FETCH_SUCCESS:
- return {
- ...state,
- latitude: action.userCoordinates.latitude,
- longitude: action.userCoordinates.longitude,
- isLoading: false,
- };
- case actionTypes.LOCATION.FETCH_FAILURE:
- return { ...state, isLoading: false };
-
- case actionTypes.INITIALIZE_STATE:
- return {
- ...state,
- stakeholders: action.stakeholders,
- ...action.payload,
- isLoading: false,
- isSearchPanelOpen: false,
- };
- case actionTypes.TOGGLE_SEARCH_PANEL:
- return { ...state, isSearchPanelOpen: action.isOpen };
- // case actionTypes.UPDATE_CRITERIA:
- // return { ...state, ...action.payload };
- default:
- return state;
- }
-}
diff --git a/client/src/hooks/useStakeholders/useStakeholders.js b/client/src/hooks/useStakeholders/useStakeholders.js
deleted file mode 100644
index aca1308b8..000000000
--- a/client/src/hooks/useStakeholders/useStakeholders.js
+++ /dev/null
@@ -1,145 +0,0 @@
-import { useReducer, useEffect } from "react";
-import * as stakeholderService from "../../services/stakeholder-service";
-import { useCategories } from "../useCategories/useCategories";
-import { actionTypes } from "./actionTypes";
-import { reducer } from "./reducer";
-import { initialState } from "./initialState";
-import queryString from "query-string";
-
-export function useStakeholders(history, userCoordinates) {
- const { data: categories } = useCategories();
- const [state, dispatch] = useReducer(reducer, initialState);
-
- // eslint-disable-next-line react-hooks/exhaustive-deps
- const search = async (
- searchString,
- latitude,
- longitude,
- selectedLocationName,
- selectedCategories,
- selectedDistance
- ) => {
- const {
- FETCH_FAILURE,
- FETCH_REQUEST,
- FETCH_SUCCESS,
- } = actionTypes.STAKEHOLDERS;
- if (!selectedCategories) return;
- try {
- dispatch({ type: FETCH_REQUEST });
- const stakeholders = await stakeholderService.search({
- name: searchString,
- categoryIds: selectedCategories.map((category) => category.id),
- latitude,
- longitude,
- distance: selectedDistance,
- });
- dispatch({
- type: FETCH_SUCCESS,
- stakeholders,
- payload: {
- searchString,
- selectedLatitude: latitude,
- selectedLongitude: longitude,
- selectedLocationName,
- selectedCategories,
- selectedDistance,
- },
- });
- history.push(
- `/stakeholders?name=${searchString}` +
- `&radius=${selectedDistance}` +
- `&lat=${latitude}` +
- `&lon=${longitude}` +
- `&placeName=${selectedLocationName}` +
- `&categoryIds=${selectedCategories.map((c) => c.id).join(",")}`
- );
- } catch (err) {
- console.log(err);
- dispatch({ type: FETCH_FAILURE });
- }
- };
-
- const applyQueryStringParameters = (history, initialState) => {
- // The goal here is to overwrite the initialState with
- // search criteria from the query string parameters, if
- // supplied. The effect should only run once when the
- // this hook loads, after the list of categories has loaded.
- let {
- searchString,
- selectedLatitude,
- selectedLongitude,
- selectedLocationName,
- selectedDistance,
- selectedCategoryIds,
- } = initialState;
-
- const params = queryString.parse(history.location.search);
-
- // override initial search parameters with any
- // query string parameters
- searchString = params.name || searchString;
- selectedDistance = params.radius || selectedDistance;
- selectedLatitude = Number.parseFloat(params.lat) || selectedLatitude;
- selectedLongitude = Number.parseFloat(params.lon) || selectedLongitude;
- selectedLocationName = params.placeName ? decodeURI(params.placeName) : "";
- if (params.categoryIds) {
- selectedCategoryIds = params.categoryIds.split(",");
- }
-
- dispatch({
- type: actionTypes.INITIALIZE_STATE,
- payload: {
- searchString,
- selectedLatitude: selectedLatitude,
- selectedLongitude: selectedLongitude,
- selectedLocationName,
- selectedCategoryIds,
- selectedDistance,
- queryParametersLoaded: true,
- },
- });
- };
-
- // useEffect(() => {
-
- // fetchCategories();
-
- // }, []);
-
- useEffect(() => {
- applyQueryStringParameters(history, initialState);
- }, [history]);
-
- useEffect(() => {
- // if we don't have the categories fetched yet, bail
- if (!categories) return;
-
- // If the query string parameters have not been applied, bail
- if (!state.queryParametersLoaded) return;
-
- let {
- searchString,
- selectedLatitude,
- selectedLongitude,
- selectedLocationName,
- selectedDistance,
- selectedCategoryIds,
- } = state;
-
- let selectedCategories = selectedCategoryIds.map(
- (id) => categories.filter((cat) => cat.id === Number(id))[0]
- );
-
- search(
- searchString,
- selectedLatitude,
- selectedLongitude,
- selectedLocationName,
- selectedCategories,
- selectedDistance
- );
- }, [categories, state.queryParametersLoaded, state, search]);
-
- return { state: { ...state, categories }, dispatch, actionTypes, search };
-}
diff --git a/client/src/images/mealIcon.js b/client/src/images/mealIcon.js
index d00a839b2..c20ad9fa2 100644
--- a/client/src/images/mealIcon.js
+++ b/client/src/images/mealIcon.js
@@ -1,10 +1,10 @@
import React from "react";
-import { mealProgram, closed } from "../theme/colors";
+import { mealProgram, closed } from "theme/colors";
-const mealIcon = (isClosed) => (
+const MealIcon = ({ isClosed, height = "72px", width = "72px" }) => (
);
-export default mealIcon;
+export default MealIcon;
diff --git a/client/src/images/pantryIcon.js b/client/src/images/pantryIcon.js
index a2f65e263..abb70bfe5 100644
--- a/client/src/images/pantryIcon.js
+++ b/client/src/images/pantryIcon.js
@@ -1,10 +1,10 @@
import React from "react";
-import { foodPantry, closed } from "../theme/colors";
+import { foodPantry, closed } from "theme/colors";
-const pantryIcon = (isClosed) => (
+const PantryIcon = ({ isClosed, height = "72px", width = "72px" }) => (
);
-export default pantryIcon;
+export default PantryIcon;
diff --git a/client/src/images/splitPantryMealIcon.js b/client/src/images/splitPantryMealIcon.js
index 27ee74332..552a2f305 100644
--- a/client/src/images/splitPantryMealIcon.js
+++ b/client/src/images/splitPantryMealIcon.js
@@ -1,14 +1,14 @@
import React from "react";
-import { foodPantry, mealProgram, closed } from "../theme/colors";
+import { foodPantry, mealProgram, closed } from "theme/colors";
-const splitPantryMealIcon = (isClosed) => (
+const SplitPantryMealIcon = ({ isClosed, height = "72px", width = "72px" }) => (
);
-export default splitPantryMealIcon;
+export default SplitPantryMealIcon;
diff --git a/client/src/secrets.js b/client/src/secrets.js
index 553e16322..48a43d98c 100644
--- a/client/src/secrets.js
+++ b/client/src/secrets.js
@@ -1,2 +1 @@
-export const MAPBOX_TOKEN =
- "pk.eyJ1IjoibHVjYXNob21lciIsImEiOiJjazFqcnRjcm0wNmZ1M2JwZXg2eDFzMXd3In0.yYpkKLrFCxF-qyBfZH1a8w";
+export const MAPBOX_TOKEN = process.env.REACT_APP_MAPBOX_TOKEN;
diff --git a/client/src/services/stakeholder-best-service.js b/client/src/services/stakeholder-best-service.js
new file mode 100644
index 000000000..85a7d590a
--- /dev/null
+++ b/client/src/services/stakeholder-best-service.js
@@ -0,0 +1,61 @@
+import axios from "axios";
+import moment from "moment";
+import { getTenantId } from "helpers/Configuration";
+
+const baseUrl = "/api/stakeholderbests";
+
+const toLocalMoment = (ts) => {
+ return !ts ? null : moment.utc(ts).local();
+};
+
+/*
+ searchParams is an object with any/all of the following properties:
+ name - look for this string as substring of the stakeholder's name
+ categoryIds - array of integers corresponding to the desired categories
+ latitude
+ longitude
+ distance - radius around latitude and longitude
+ isAssigned - ("yes", "no", "either")
+ isSubmitted - ("yes", "no", "either")
+ isApproved - ("yes", "no", "either")
+ isClaimed - ("yes", "no", "either")
+ assignedLoginId
+ claimedLoginId
+*/
+export const search = async (searchParams) => {
+ searchParams = searchParams || {};
+ const response = await axios.get(baseUrl, {
+ params: {
+ ...searchParams,
+ tenantId: getTenantId(),
+ },
+ });
+ let stakeholders = response.data.map((s) => {
+ return {
+ ...s,
+ createdDate: toLocalMoment(s.createdDate),
+ modifiedDate: toLocalMoment(s.modifiedDate),
+ assignedDate: toLocalMoment(s.assignedDate),
+ submittedDate: toLocalMoment(s.submittedDate),
+ approvedDate: toLocalMoment(s.approvedDate),
+ claimedDate: toLocalMoment(s.claimedDate),
+ };
+ });
+
+ // console.log("stakeholders", stakeholders);
+ return stakeholders;
+};
+
+export const getById = async (id) => {
+ const response = await axios.get(`${baseUrl}/${id}`);
+ const s = response.data;
+ return {
+ ...s,
+ createdDate: toLocalMoment(s.createdDate),
+ modifiedDate: toLocalMoment(s.modifiedDate),
+ assignedDate: toLocalMoment(s.assignedDate),
+ submittedDate: toLocalMoment(s.submittedDate),
+ approvedDate: toLocalMoment(s.approvedDate),
+ claimedDate: toLocalMoment(s.claimedDate),
+ };
+};
diff --git a/client/src/services/stakeholder-service.js b/client/src/services/stakeholder-service.js
index 7b95e6a00..4b48717de 100644
--- a/client/src/services/stakeholder-service.js
+++ b/client/src/services/stakeholder-service.js
@@ -9,47 +9,9 @@ const toLocalMoment = (ts) => {
return !ts ? null : moment.utc(ts).local();
};
-/*
- searchParams is an object with any/all of the following properties:
- name - look for this string as substring of the stakeholder's name
- categoryIds - array of integers corresponding to the desired categories
- latitude
- longitude
- distance - radius around latitude and longitude
- isAssigned - ("yes", "no", "either")
- isSubmitted - ("yes", "no", "either")
- isApproved - ("yes", "no", "either")
- isClaimed - ("yes", "no", "either")
- assignedLoginId
- claimedLoginId
-*/
export const search = async (searchParams) => {
searchParams = searchParams || {};
- const response = await axios.get(baseUrl, {
- params: {
- ...searchParams,
- tenantId: getTenantId(),
- },
- });
- let stakeholders = response.data.map((s) => {
- return {
- ...s,
- createdDate: toLocalMoment(s.createdDate),
- modifiedDate: toLocalMoment(s.modifiedDate),
- assignedDate: toLocalMoment(s.assignedDate),
- submittedDate: toLocalMoment(s.submittedDate),
- approvedDate: toLocalMoment(s.approvedDate),
- claimedDate: toLocalMoment(s.claimedDate),
- };
- });
-
- // console.log("stakeholders", stakeholders);
- return stakeholders;
-};
-
-export const searchDashboard = async (searchParams) => {
- searchParams = searchParams || {};
- const response = await axios.get(`${baseUrl}/dashboard`, {
+ const response = await axios.get(`${baseUrl}`, {
params: searchParams,
});
let stakeholders = response.data.map((s) => {
diff --git a/package.json b/package.json
index 9f6573149..4f77b5818 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "foodoasis",
- "version": "1.0.21",
+ "version": "1.0.22",
"author": "Hack for LA",
"description": "Web API Server for Food Oasis",
"main": "server.js",