From e4cd7ec20dd87ffa4313d020d8ad811c3c489886 Mon Sep 17 00:00:00 2001 From: Daniel Ichiyama Date: Wed, 11 Nov 2020 14:24:46 -1000 Subject: [PATCH 1/6] changes redux connect to redux hooks in retailerView --- src/actions/index.js | 2 +- src/views/RetailerView/RetailerView.jsx | 66 ++++++++++--------------- 2 files changed, 28 insertions(+), 40 deletions(-) diff --git a/src/actions/index.js b/src/actions/index.js index 854ecb1..f92d82d 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -61,7 +61,7 @@ export const searchRetailersByName = (data) => async (dispatch) => { }); }; -export const fetchOneRetailer = (id) => async (dispatch) => { +export const actionFetchOneRetailer = (id) => async (dispatch) => { // needs to be replaced later with dynamically generated ids await Axios.get(`/api/retailers/${id}`) .then((retailer) => { diff --git a/src/views/RetailerView/RetailerView.jsx b/src/views/RetailerView/RetailerView.jsx index e0322d0..143b72c 100644 --- a/src/views/RetailerView/RetailerView.jsx +++ b/src/views/RetailerView/RetailerView.jsx @@ -1,13 +1,26 @@ import React, { useState, useEffect } from "react"; import styles from "./RetailerView.module.scss"; -import { connect } from "react-redux"; -import { fetchOneRetailer, actionUpdateRetailer } from "../../actions"; +import { useDispatch, useSelector } from "react-redux"; +import { actionFetchOneRetailer, actionUpdateRetailer } from "../../actions"; import Dashboard from "../../components/Dashboard"; // import Profile from "../../components/Profile"; import WaitList from "../../components/WaitList/WaitList.jsx"; import HoldList from "../../components/HoldList/HoldList.jsx"; function RetailerView(props) { + const dispatch = useDispatch(); + const waitList = useSelector(state=> state.currentRetailer.reservations.filter((res) => { + return res.queueStatus === "wait"; + })) + const holdList = useSelector(state=>state.currentRetailer.reservations.filter((res) => { + return res.queueStatus === "hold"; + })) + const retailerName = useSelector(state=>state.currentRetailer + ? state.currentRetailer.retailerName + : null) + + const retailer = useSelector(state=>state.currentRetailer); + const [maxCapacity] = useState(JSON.parse(localStorage.retailer).maxCapacity); const [initialCount] = useState( parseInt(localStorage.getItem("currentCapacity")) || 0 @@ -44,17 +57,18 @@ function RetailerView(props) { countElement.innerHTML = `${maxCapacity}(${overflow})`; } }, [custCount, maxCapacity]); - let { changeCustomersInStore } = props; + //handles updating customer count to db useEffect(() => { let retailerId = JSON.parse(localStorage.retailer).id; + if (custCount === maxCapacity) { - return changeCustomersInStore({ currentCapacity: custCount }, retailerId); + dispatch(actionUpdateRetailer({ currentCapacity: custCount }, retailerId)); } if (custCount === maxCapacity - 1) { - return changeCustomersInStore({ currentCapacity: custCount }, retailerId); + dispatch(actionUpdateRetailer({ currentCapacity: custCount }, retailerId)); } - }, [custCount, maxCapacity, changeCustomersInStore]); + }, [custCount, maxCapacity, dispatch]); // handles open and close of On Hold list const [isOpen, setIsOpen] = useState(false); @@ -87,24 +101,23 @@ function RetailerView(props) { }; // grabs the initial data when the view loads - const { dispatchFetchOneRetailer } = props; useEffect(() => { let retailer = JSON.parse(localStorage.getItem("retailer")) if (retailer) { - return dispatchFetchOneRetailer(retailer.id); + dispatch(actionFetchOneRetailer(retailer.id)); } - }, [dispatchFetchOneRetailer]); + }, [dispatch]); return ( <>
{/* Needs to be connected to a button to access retailer profile. */} - {/* */} + {/* */} ); } -const mapStateToProps = (state) => { - return { - waitList: state.currentRetailer.reservations.filter((res) => { - return res.queueStatus === "wait"; - }), - holdList: state.currentRetailer.reservations.filter((res) => { - return res.queueStatus === "hold"; - }), - retailerName: state.currentRetailer - ? state.currentRetailer.retailerName - : null, - retailer: state.currentRetailer, - }; -}; - -const mapDispatchToProps = (dispatch) => { - return { - dispatchFetchOneRetailer: (id) => { - dispatch(fetchOneRetailer(id)); - }, - changeCustomersInStore: (data, id) => { - dispatch(actionUpdateRetailer(data, id, true)); - }, - }; -}; -export default connect(mapStateToProps, mapDispatchToProps)(RetailerView); +export default RetailerView; From 9de5d5a6e470b2a50a8da2543be888de65ed0a86 Mon Sep 17 00:00:00 2001 From: Daniel Ichiyama Date: Wed, 11 Nov 2020 15:23:43 -1000 Subject: [PATCH 2/6] refactors all redux connects to redux hooks --- src/actions/index.js | 4 +-- src/components/Modal/Modal.jsx | 20 ++++------- src/components/Reservation/Reservation.jsx | 35 ++++++------------- src/views/UserView/UserView.jsx | 39 ++++++---------------- 4 files changed, 29 insertions(+), 69 deletions(-) diff --git a/src/actions/index.js b/src/actions/index.js index f92d82d..21ede51 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -103,7 +103,7 @@ export const actionUpdateReservation = (data, id) => async (dispatch) => { }); }; -export const notifyCustomer = (data) => async (dispatch) => { +export const actionNotifyCustomer = (data) => async (dispatch) => { await Axios.post("/api/sms/send", data) .then((response) => { dispatch({ @@ -179,7 +179,7 @@ export const actionFindRetailers = () => async (dispatch) => { }); }; -export const actionSearchingRetailers = (searchTerm) => async (dispatch) => { +export const actionSearchRetailers = (searchTerm) => async (dispatch) => { await Axios.get("/api/customers/search?searchTerm=" + searchTerm) .then((retailers) => { dispatch({ diff --git a/src/components/Modal/Modal.jsx b/src/components/Modal/Modal.jsx index e490c88..2bda5cd 100644 --- a/src/components/Modal/Modal.jsx +++ b/src/components/Modal/Modal.jsx @@ -1,18 +1,10 @@ -import React, { Component } from "react"; +import React from "react"; import styles from "./Modal.module.scss"; -class Modal extends Component { - constructor(props) { - super(props); - this.state = {}; - } - render() { - return ( -
-
{this.props.children}
-
- ); - } +const Modal = (props) => { + return (
+
{this.props.children}
+
); } - + export default Modal; diff --git a/src/components/Reservation/Reservation.jsx b/src/components/Reservation/Reservation.jsx index 7c71696..6d80bf9 100644 --- a/src/components/Reservation/Reservation.jsx +++ b/src/components/Reservation/Reservation.jsx @@ -6,13 +6,16 @@ import CheckButton from "../CheckButton"; import HoldButton from "../HoldButton"; import NotificationButton from "../NotificationButton"; import moment from "moment"; -import { connect } from "react-redux"; +import { useDispatch, useSelector } from "react-redux"; import { - notifyCustomer, + actionNotifyCustomer, actionUpdateReservation } from "../../actions"; const Reservation = (props) => { + const dispatch = useDispatch(); + const retailerName = useSelector(state=>state.currentRetailer.retailerName); + const [menuOpen, setMenuOpen] = useState(false); const [confirmOpen, setConfirmOpen] = useState(false); @@ -55,27 +58,27 @@ const Reservation = (props) => { const handleNotificationClick = () => { let data = { phoneNumber: props.reservation.customerId.phoneNumber, - retailerName: props.retailerName, + retailerName: retailerName, reservationId: props.reservation._id, }; - return props.dispatchNotifyCustomer(data); + return dispatch(actionNotifyCustomer(data)); }; const handleHoldClick = () => { let data = { queueStatus: "hold", }; - props.dispatchUpdateReservation(data, props.reservation.id); + dispatch(actionUpdateReservation(data, props.reservation.id)); return toggleMenu(); }; const handleRemoveCustomer = () => { let data = { queueStatus: 'cancelled' }; - props.dispatchUpdateReservation(data, props.reservation.id); + dispatch(actionUpdateReservation(data, props.reservation.id)); return toggleMenu(); }; const handleCheckinCustomer = () => { let data = { queueStatus: 'enter' }; - props.dispatchUpdateReservation(data, props.reservation.id); + dispatch(actionUpdateReservation(data, props.reservation.id)); props.handlePlusPartySize(props.reservation.partySize); return toggleMenu(); }; @@ -151,21 +154,5 @@ const Reservation = (props) => { ); }; -const mapStateToProps = (state) => { - return { - retailerName: state.currentRetailer.retailerName, - }; -}; - -const mapDispatchToProps = (dispatch) => { - return { - dispatchNotifyCustomer: (data) => { - return dispatch(notifyCustomer(data)); - }, - dispatchUpdateReservation: (data, id) => { - return dispatch(actionUpdateReservation(data, id)); - } - }; -}; -export default connect(mapStateToProps, mapDispatchToProps)(Reservation); +export default Reservation; diff --git a/src/views/UserView/UserView.jsx b/src/views/UserView/UserView.jsx index 8d0f812..b37ccb7 100644 --- a/src/views/UserView/UserView.jsx +++ b/src/views/UserView/UserView.jsx @@ -1,29 +1,25 @@ import React from "react"; import styles from "./UserView.module.scss"; import magGlass from "../../utils/imgs/magGlass.png"; -import { connect } from "react-redux"; -import { actionFindRetailers, actionSearchingRetailers } from "../../actions"; +import { useDispatch, useSelector } from "react-redux"; +import { actionFindRetailers, actionSearchRetailers } from "../../actions"; import RetailerCard from "../../components/RetailerCard/RetailerCard"; import { useEffect, useState } from "react"; function UserView(props) { + const dispatch = useDispatch(); + const customerSearchRetailer = useSelector(state=> state.customerSearchRetailer); const [searchTerm, setSearchTerm] = useState(''); - const [foundRetailers, setFoundRetailers] = useState([]); //on mount find all retailers - let {dispatchFindRetailers} = props useEffect(() => { - return dispatchFindRetailers(); - }, [dispatchFindRetailers]); - - useEffect(() => { - return setFoundRetailers(props.customerSearchRetailer); - }, [props.customerSearchRetailer]); + dispatch(actionFindRetailers()); + }, []); const handleSearchSubmit = (e) => { e.preventDefault(); if (searchTerm !== '') { - props.dispatchSearchingRetailers(searchTerm); + dispatch(actionSearchRetailers(searchTerm)); } return setSearchTerm(''); }; @@ -51,9 +47,9 @@ function UserView(props) { search - {foundRetailers.length === 0 ?

{"No search results"}

: null} + {customerSearchRetailer.length === 0 ?

{"No search results"}

: null}
    - {foundRetailers.map((store, index) => { + {customerSearchRetailer.map((store, index) => { return ( { - return { - customerSearchRetailer: [...state.customerSearchRetailer], - }; -}; -const mapDispatchToProps = (dispatch) => { - return { - dispatchFindRetailers: () => { - return dispatch(actionFindRetailers()); - }, - dispatchSearchingRetailers: (term) => { - return dispatch(actionSearchingRetailers(term)); - }, - }; -}; -export default connect(mapStateToProps, mapDispatchToProps)(UserView); +export default UserView; From bf32a253dee222e9516365ff1f2284d98603c529 Mon Sep 17 00:00:00 2001 From: Daniel Ichiyama Date: Wed, 11 Nov 2020 17:00:52 -1000 Subject: [PATCH 3/6] adds useEffect to seta value in state to hold formatted time for reservations so that from now time is updated every minute --- src/components/Login/Login.jsx | 4 +- src/components/Reservation/Reservation.jsx | 47 +++++++++++++--------- 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/src/components/Login/Login.jsx b/src/components/Login/Login.jsx index 34e95d6..ebc6fc3 100644 --- a/src/components/Login/Login.jsx +++ b/src/components/Login/Login.jsx @@ -22,7 +22,7 @@ function Login(props) {
    loginSubmit(e, { username, password })}>
    • - +
    • - + { const [menuOpen, setMenuOpen] = useState(false); const [confirmOpen, setConfirmOpen] = useState(false); + const [formattedTime, setFormattedTime] = useState(moment(props.reservation.createdAt).fromNow()); const toggleMenu = () => { setMenuOpen(!menuOpen); @@ -40,6 +60,13 @@ const Reservation = (props) => { document.addEventListener("mousedown", handleClickOutside); }); + useEffect(() => { + const interval = setInterval(() => { + setFormattedTime(moment(props.reservation.createdAt).fromNow()); + }, 60000); + return () => clearInterval(interval); + }, [props.reservation.createdAt]); + const toggleConfirm = () => { setConfirmOpen(!confirmOpen); @@ -79,28 +106,10 @@ const Reservation = (props) => { props.handlePlusPartySize(props.reservation.partySize); return toggleMenu(); }; + const { isHold } = props; const { phoneNumber } = props.reservation.customerId; const phone = (phoneNumber).replace(/\W\d(\d\d\d)(\d\d\d)(\d\d\d\d)/, '($1) $2-$3'); - moment.locale('en', { - relativeTime: { - future: 'in %s', - past: '%s', - s: 'Just now', - ss: 'Just now', - m: '1m ago', - mm: '%dm ago', - h: '1h', - hh: '%dh ago', - d: '1d', - dd: '%dd ago', - M: '1m', - MM: '%dM ago', - y: '1y', - yy: '%dY ago' - } - }); - let formattedTime = moment(props.reservation.createdAt).fromNow(); return (
    • From 4f5c14e4ec033ac10bbb9678b5d8ba8a4f7c60ed Mon Sep 17 00:00:00 2001 From: Daniel Ichiyama Date: Wed, 11 Nov 2020 17:12:25 -1000 Subject: [PATCH 4/6] adds comment to explain timer rerenderer in reservation component --- src/components/Reservation/Reservation.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/Reservation/Reservation.jsx b/src/components/Reservation/Reservation.jsx index a2e0814..3923555 100644 --- a/src/components/Reservation/Reservation.jsx +++ b/src/components/Reservation/Reservation.jsx @@ -60,12 +60,14 @@ const Reservation = (props) => { document.addEventListener("mousedown", handleClickOutside); }); + // rerenders the component every minute to update the fromNow time useEffect(() => { const interval = setInterval(() => { setFormattedTime(moment(props.reservation.createdAt).fromNow()); }, 60000); return () => clearInterval(interval); }, [props.reservation.createdAt]); + const toggleConfirm = () => { setConfirmOpen(!confirmOpen); From b520be8c2324133d2f96ebe595d03cb09e97584e Mon Sep 17 00:00:00 2001 From: Daniel Ichiyama Date: Wed, 11 Nov 2020 17:37:46 -1000 Subject: [PATCH 5/6] adds confirmation menu to reservation menu options --- src/components/Reservation/Reservation.jsx | 45 +++++++++++++--------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/src/components/Reservation/Reservation.jsx b/src/components/Reservation/Reservation.jsx index 6dd7e64..ac88eca 100644 --- a/src/components/Reservation/Reservation.jsx +++ b/src/components/Reservation/Reservation.jsx @@ -38,6 +38,7 @@ const Reservation = (props) => { const [menuOpen, setMenuOpen] = useState(false); const [confirmOpen, setConfirmOpen] = useState(false); const [formattedTime, setFormattedTime] = useState(moment(props.reservation.createdAt).fromNow()); + const [queueStatus, setQueueStatus]= useState(""); const toggleMenu = () => { setMenuOpen(!menuOpen); @@ -79,37 +80,45 @@ const Reservation = (props) => { }; const handleConfirm = () => { - // this is where we need to send the data to the backend - + let data; + if (queueStatus !=="pending"){ + data = {queueStatus}; + dispatch(actionUpdateReservation(data, props.reservation.id)); + if (queueStatus === "enter"){ + props.handlePlusPartySize(props.reservation.partySize); + } + } else { + data ={ + phoneNumber: props.reservation.customerId.phoneNumber, + retailerName: retailerName, + reservationId: props.reservation._id, + }; + dispatch (actionNotifyCustomer(data)) + } + setConfirmOpen(false); }; const handleNotificationClick = () => { - let data = { - phoneNumber: props.reservation.customerId.phoneNumber, - retailerName: retailerName, - reservationId: props.reservation._id, - }; - return dispatch(actionNotifyCustomer(data)); + + return toggleConfirm(); }; const handleHoldClick = () => { - let data = { queueStatus: "hold", }; - dispatch(actionUpdateReservation(data, props.reservation.id)); - return toggleMenu(); + setQueueStatus("hold"); + return toggleConfirm(); + }; const handleRemoveCustomer = () => { - let data = { queueStatus: 'cancelled' }; - dispatch(actionUpdateReservation(data, props.reservation.id)); - return toggleMenu(); + setQueueStatus("cancelled") + + return toggleConfirm(); }; const handleCheckinCustomer = () => { - let data = { queueStatus: 'enter' }; - dispatch(actionUpdateReservation(data, props.reservation.id)); - props.handlePlusPartySize(props.reservation.partySize); - return toggleMenu(); + setQueueStatus("enter"); + return toggleConfirm(); }; const { isHold } = props; From 4fe6c4678c629b2fc11da4fbdecd5438cdea5e39 Mon Sep 17 00:00:00 2001 From: Daniel Ichiyama Date: Wed, 11 Nov 2020 17:49:56 -1000 Subject: [PATCH 6/6] save point --- src/components/Reservation/Reservation.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Reservation/Reservation.jsx b/src/components/Reservation/Reservation.jsx index ac88eca..8c169cb 100644 --- a/src/components/Reservation/Reservation.jsx +++ b/src/components/Reservation/Reservation.jsx @@ -79,6 +79,7 @@ const Reservation = (props) => { toggleMenu(); }; + const handleConfirm = () => { let data; if (queueStatus !=="pending"){