diff --git a/src/components/DateSelector/DateSelector.jsx b/src/components/DateSelector/DateSelector.jsx index a03f098af..ef6cd16ea 100644 --- a/src/components/DateSelector/DateSelector.jsx +++ b/src/components/DateSelector/DateSelector.jsx @@ -1,4 +1,4 @@ -import React, { useState, useCallback } from 'react'; +import React, { useState, useEffect, useCallback } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import moment from 'moment'; @@ -13,6 +13,7 @@ import ArrowToolTip from '@components/common/ArrowToolTip'; import options from './options'; import useStyles from './useStyles'; import DateRanges from './DateRanges'; +import { updateEndDate, updateStartDate } from '../../redux/reducers/filters'; const dateFormat = 'YYYY-MM-DD'; @@ -20,6 +21,9 @@ function DateSelector({ range, updateStartDate, updateEndDate, + startDate, + endDate, + hasError = false // Form Validation for blank map }) { const [expandedMenu, setExpandedMenu] = useState(false); const classes = useStyles(); @@ -44,7 +48,7 @@ function DateSelector({ return ( <> - + Date Range 
@@ -64,12 +68,19 @@ function DateSelector({
- setExpandedMenu(!expandedMenu)} expanded={expandedMenu}> +
+ setExpandedMenu(!expandedMenu)} expanded={expandedMenu} style={{border: hasError ? '1.3px solid #DE2800': undefined}}>
-
@@ -82,23 +93,31 @@ function DateSelector({ /> +
); } +const mapStateToProps = state => ({ + startDate: state.filters.startDate, + endDate: state.filters.endDate +}); + const mapDispatchToProps = dispatch => ({ updateStartDate: date => dispatch(reduxUpdateStartDate(date)), updateEndDate: date => dispatch(reduxUpdateEndDate(date)), }); -export default connect(null, mapDispatchToProps)(DateSelector); +export default connect(mapStateToProps, mapDispatchToProps)(DateSelector); DateSelector.propTypes = { range: PropTypes.bool, updateStartDate: PropTypes.func.isRequired, updateEndDate: PropTypes.func.isRequired, + hasError: PropTypes.bool, }; DateSelector.defaultProps = { range: false, + hasError: false, }; diff --git a/src/components/common/DatePicker/DatePicker.jsx b/src/components/common/DatePicker/DatePicker.jsx index 5d3dc97af..19416dc2e 100644 --- a/src/components/common/DatePicker/DatePicker.jsx +++ b/src/components/common/DatePicker/DatePicker.jsx @@ -99,16 +99,23 @@ function DatePicker({ () => { if (startDate && endDate){ setShowCalendar(false); - } else if (startDate && !endDate){ - // The calendar was closed with an incomplete date range selection so we need to restart - // startDate and endDate to their initial values - updateStartDate(initialStartDate); - updateEndDate(initialEndDate); - setShowCalendar(false); + // For Blank Map Implementation: no need to reset date range selection for testing purposes for date form validation + // } else if (startDate && !endDate){ + // // The calendar was closed with an incomplete date range selection so we need to restart + // // startDate and endDate to their initial values + // updateStartDate(initialStartDate); + // updateEndDate(initialEndDate); + // setShowCalendar(false); } else { // This should never happen. Log a warning. console.warn('Try to set a new date selection. Dates were in an invalid state. StartDate: ', startDate, " endDate: ", endDate); } + + //Blank Map Implementation + if (startDate == null || endDate == null){ + setShowCalendar(false); + } + }, [startDate, endDate]); useOutsideClick(ref, closeCalendar); @@ -139,8 +146,10 @@ function DatePicker({ const toggleCalendar = () => { if (showCalendar) { + console.log("toggle calendar - close") closeCalendar(); } else { + console.log("toggle calendar - open") openCalendar(); } if (onTogglePresets) onTogglePresets(); diff --git a/src/components/common/ReactDayPicker/ReactDayPicker.jsx b/src/components/common/ReactDayPicker/ReactDayPicker.jsx index b1569fcd8..fbf856680 100644 --- a/src/components/common/ReactDayPicker/ReactDayPicker.jsx +++ b/src/components/common/ReactDayPicker/ReactDayPicker.jsx @@ -157,6 +157,7 @@ function ReactDayPicker({ // enteredTo represents the day that the user is currently hovering over. const [enteredTo, setEnteredTo] = useState(endDate); + // Sets start date const setFromDay = day => { updateStartDate(moment(day).format(INTERNAL_DATE_SPEC)); }; @@ -166,13 +167,26 @@ function ReactDayPicker({ }; const handleDayClick = day => { - if (!range) { - setFromDay(day); - return; - } - - // If both startDate and endDate were already selected. Start a new range selection. - if (startDate && endDate){ + //Blank Map Implementation + console.warn('Current start date: ', startDate); + console.warn('Current end date: ', endDate); + var selectedDate = moment(day).format(INTERNAL_DATE_SPEC) + console.warn('Date selected: ', selectedDate); + + // Initial state: null startDate and endDate, user first selects start date + if(!startDate){ + console.warn('Start Date Selected: ', day); + setFromDay(day); + // Case: missing start date, user unselects start date + } else if(startDate && startDate == selectedDate) { + console.warn('Same start date selected, remove start date: ', selectedDate); + updateStartDate(null); + // Case: missing end date, user unselects end date + } else if(endDate && endDate == selectedDate) { + console.warn('Same end date selected, remove end date: ', selectedDate); + updateEndDate(null); + } // If both startDate and endDate were already selected. Start a new range selection. + else if (startDate && endDate){ setFromDay(day); updateEndDate(null); setEnteredTo(null); @@ -191,7 +205,7 @@ function ReactDayPicker({ } } else { // This should never happen. Log a warning. - console.warn('Try to set a new date selection. Dates were in an invalid state. StartDate: ', startDate, " endDate: ", endDate); + console.warn('ReactDayPicker: Try to set a new date selection. Dates were in an invalid state. StartDate: ', startDate, " endDate: ", endDate); } }; diff --git a/src/components/layout/Main/Desktop/CouncilSelector/index.jsx b/src/components/layout/Main/Desktop/CouncilSelector/index.jsx index 6f6199d4b..07afab5d6 100644 --- a/src/components/layout/Main/Desktop/CouncilSelector/index.jsx +++ b/src/components/layout/Main/Desktop/CouncilSelector/index.jsx @@ -34,6 +34,7 @@ function CouncilSelector({ dispatchCloseBoundaries, resetMap, resetAddressSearch, + hasError = false // Form Validation for blank map }) { const classes = useStyles(); const [searchTerm, setSearchTerm] = useState(''); @@ -65,10 +66,10 @@ function CouncilSelector({ const newSelected = [newSelectedCouncil]; dispatchUpdateSelectedCouncils(newSelected); dispatchUpdateUnselectedCouncils(councils); - dispatchUpdateNcId(selectedCouncilId); + // dispatchUpdateNcId(selectedCouncilId); // Triggers zoom // Collapse boundaries section - dispatchCloseBoundaries(); + // dispatchCloseBoundaries(); } }; @@ -78,9 +79,14 @@ function CouncilSelector({ return ( <> - Boundaries + + Boundaries + +
- + @@ -89,6 +95,7 @@ function CouncilSelector({ )} +
); } @@ -115,6 +122,7 @@ CouncilSelector.defaultProps = { resetMap: () => {}, resetAddressSearch: () => {}, dispatchCloseBoundaries: undefined, + hasError: false, }; CouncilSelector.propTypes = { @@ -127,4 +135,5 @@ CouncilSelector.propTypes = { resetMap: PropTypes.func, resetAddressSearch: PropTypes.func, dispatchCloseBoundaries: PropTypes.func, + hasError: PropTypes.bool, }; diff --git a/src/components/layout/Main/Desktop/Export/ExportButton.jsx b/src/components/layout/Main/Desktop/Export/ExportButton.jsx index b7816f5ea..5aba0344f 100644 --- a/src/components/layout/Main/Desktop/Export/ExportButton.jsx +++ b/src/components/layout/Main/Desktop/Export/ExportButton.jsx @@ -19,7 +19,7 @@ import requestTypes from '@data/requestTypes'; import useStyles from './useStyles'; import ExportDialog from './ExportDialog'; -function ExportButton({ filters }) { +function ExportButton({ filters, disabled = false }) { const { conn } = useContext(DbContext); const classes = useStyles(); const [showDialog, setShowDialog] = useState(false); @@ -274,9 +274,9 @@ function ExportButton({ filters }) { return ( <> - @@ -317,4 +317,5 @@ ExportButton.propTypes = { }), requestTypes: PropTypes.objectOf(PropTypes.bool), }).isRequired, + disabled: PropTypes.bool, }; diff --git a/src/components/layout/Main/Desktop/Export/useStyles.js b/src/components/layout/Main/Desktop/Export/useStyles.js index 8be08301d..3883e7c39 100644 --- a/src/components/layout/Main/Desktop/Export/useStyles.js +++ b/src/components/layout/Main/Desktop/Export/useStyles.js @@ -2,12 +2,18 @@ import makeStyles from '@mui/styles/makeStyles'; const useStyles = makeStyles(theme => ({ exportButton: { + width: 300, + justifyContent: 'center', color: theme.palette.text.secondaryLight, - textDecoration: 'underline', + borderColor: theme.palette.text.secondaryLight, + borderRadius: 5, + backgroundColor: 'transparent', + text: 'black', + padding: 3, '&:hover': { - textDecoration: 'underline', + backgroundColor: theme.palette.text.secondaryLight, + color: 'black' }, - padding: 0, }, confirmationButton: { width: '229px', diff --git a/src/components/layout/Main/Desktop/FilterMenu.jsx b/src/components/layout/Main/Desktop/FilterMenu.jsx index 50cced406..9395d8bdd 100644 --- a/src/components/layout/Main/Desktop/FilterMenu.jsx +++ b/src/components/layout/Main/Desktop/FilterMenu.jsx @@ -1,6 +1,8 @@ import React, { useState } from 'react'; import PropTypes from 'proptypes'; import { connect } from 'react-redux'; +import { useDispatch } from 'react-redux'; +import { triggerDisplayData } from '@reducers/data'; import makeStyles from '@mui/styles/makeStyles'; import Card from '@mui/material/Card'; import CardHeader from '@mui/material/CardHeader'; @@ -18,6 +20,7 @@ import TypeSelector from '@components/layout/Main/Desktop/TypeSelector'; import StatusSelector from '@components/layout/Main/Desktop/StatusSelector'; import CouncilSelector from '@components/layout/Main/Desktop/CouncilSelector'; import ExportButton from '@components/layout/Main/Desktop/Export/ExportButton'; +import Button from '@mui/material/Button'; // import clsx from 'clsx'; @@ -61,9 +64,21 @@ const useStyles = makeStyles(theme => ({ selectorWrapper: { marginBottom: theme.gaps.md, }, + displayButton: { + width: 300, + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + marginTop: 30, + borderRadius: 5, + padding: 3, + backgroundColor: theme.palette.text.secondaryLight, + color: 'black', + text: 'black', + }, export: { - display: 'grid', - justifyContent: 'flex-end', + display: 'flex', + alignItems: 'center', }, content: { padding: '6px 14px', @@ -71,10 +86,80 @@ const useStyles = makeStyles(theme => ({ })); // const FilterMenu = ({ toggleMenu }) => { //toggleMenu used with GearButton -function FilterMenu({ resetMap, resetAddressSearch, map, geoFilterType, councils, onGeocoderResult, onChangeTab, onReset, canReset }) { +function FilterMenu({ resetMap, resetAddressSearch, map, geoFilterType, councils, onGeocoderResult, onChangeTab, onReset, canReset, + // Redux connected components + selectedCouncils, // Councils/Boundaries Form Values + startDate, endDate, // Date Range Form Values + selectedTypes, // Request Type Form Values + requestStatus, // Request Status Form Values +}) { const [expanded, setExpanded] = useState(true); const classes = useStyles(); const sharedClasses = sharedStyles(); + // Blank map implementation: form validation + const [formErrors, setFormErrors] = useState({}); + const [formValid, setFormValid] = useState(false); + const [selectedAddress, setSelectedAddress] = useState(null); + const dispatch = useDispatch(); + + const handleGeocoderResult = ( result ) => { + console.log("Filter menu - received address selected from MapSearch:", result.place_name); + if(result.place_name == '') { + setSelectedAddress(null); + } else { + setSelectedAddress(result.place_name); + } + }; + + const validateForm = () => { + let newErrors = {}; + + // Address Validation or Council Validation + console.log('Selected Address: ' + selectedAddress); + if (!selectedAddress && (!selectedCouncils || selectedCouncils.length === 0)) { + newErrors.address = 'Please search by address or neighborhood'; + newErrors.councils = 'Please select a Neighborhood'; + } + + // Date Range Validation + if (!startDate && !endDate) { + newErrors.dates = 'Please select a date range'; + } else if (!startDate && endDate) { + newErrors.dates = 'Please select a start date'; + } else if (startDate && !endDate) { + newErrors.dates = 'Please select an end date'; + } + console.log('startDate: ' + startDate); + console.log('endDate: ' + endDate); + + // Request Type Validation + const anyTypeSelected = Object.values(selectedTypes).some(val => val); + if (!anyTypeSelected) { + newErrors.types = 'Please select at least one type of request'; + } + + // Request Status Validation + if (!requestStatus.open && !requestStatus.closed) { + newErrors.status = 'Please select at least one request status'; + } + + setFormErrors(newErrors); + const isValid = Object.keys(newErrors).length === 0; + setFormValid(isValid); + return isValid; + }; + + const handleDisplayData = () => { + if (validateForm()) { + console.log("Valid form — proceeds to display data"); + // trigger data fetch + dispatch(triggerDisplayData()); + } else { + console.log("Invalid form", formErrors); + } + // setSelectedAddress(null); + }; + return ( @@ -115,29 +200,39 @@ function FilterMenu({ resetMap, resetAddressSearch, map, geoFilterType, councils map={map} geoFilterType={geoFilterType} councils={councils} - onGeocoderResult={onGeocoderResult} + onGeocoderResult={handleGeocoderResult} onChangeTab={onChangeTab} onReset={onReset} canReset={canReset} + hasError={formErrors.address ? true : false} /> + {formErrors.address && ({formErrors.address})}
- + {formErrors.councils && ({formErrors.councils})} +
+
+ + {formErrors.dates && ({formErrors.dates})}
- + + {formErrors.types && ({formErrors.types})}
- + + {formErrors.status && ( {formErrors.status} )}
- +
-
- +
+
@@ -146,11 +241,24 @@ function FilterMenu({ resetMap, resetAddressSearch, map, geoFilterType, councils ); } +// Child component props for form validation +const mapStateToProps = state => ({ + // Councils/Boundaries Form Values + selectedCouncils: state.filters.selected, + // Date Range Form Values + startDate: state.filters.startDate, + endDate: state.filters.endDate, + // Request Type Form Values + selectedTypes: state.filters.requestTypes, + // Request Status Form Values + requestStatus: state.filters.requestStatus +}); + const mapDispatchToProps = dispatch => ({ toggleMenu: () => dispatch(reduxToggleMenu()), }); -export default connect(null, mapDispatchToProps)(FilterMenu); +export default connect(mapStateToProps, mapDispatchToProps)(FilterMenu); FilterMenu.defaultProps = { resetMap: () => {}, @@ -161,7 +269,15 @@ FilterMenu.defaultProps = { onGeocoderResult: () => {}, onChangeTab: () => {}, onReset: () => {}, - canReset: false + canReset: false, + // Councils/Boundaries Form Values + selectedCouncils: null, + // Date Range Form Values + startDate: null, + endDate: null, + // Request Type Form Values + selectedTypes: {}, + // Request Status Form Values }; FilterMenu.propTypes = { @@ -174,5 +290,5 @@ FilterMenu.propTypes = { onGeocoderResult: PropTypes.func, onChangeTab: PropTypes.func, onReset: PropTypes.func, - canReset: PropTypes.bool + canReset: PropTypes.bool, }; diff --git a/src/components/layout/Main/Desktop/StatusSelector.jsx b/src/components/layout/Main/Desktop/StatusSelector.jsx index 3c24d1c5c..3fc26cec0 100644 --- a/src/components/layout/Main/Desktop/StatusSelector.jsx +++ b/src/components/layout/Main/Desktop/StatusSelector.jsx @@ -8,6 +8,12 @@ import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'; import { updateRequestStatus } from '@reducers/filters'; import ArrowToolTip from '@components/common/ArrowToolTip'; +import Box from '@mui/material/Box'; +import FormGroup from '@mui/material/FormGroup'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import Checkbox from '@mui/material/Checkbox'; +import { borderColor } from '@mui/system'; + const useStyles = makeStyles(() => ({ header: { marginBottom: 5, @@ -28,17 +34,31 @@ const useStyles = makeStyles(() => ({ function StatusSelector({ updateStatusFilter, requestStatus, + hasError = false // Form Validation for blank map }) { const classes = useStyles(); const [selection, setSelection] = useState('open'); - const handleSelection = (event, newSelection) => { - setSelection(newSelection); + const handleSelection = (status, checked) => { + const newStatus = { + ...requestStatus, + [status]: checked, + }; + + if (newStatus.open && newStatus.closed) { + updateStatusFilter('all'); + } else if (newStatus.open) { + updateStatusFilter('open'); + } else if (newStatus.closed) { + updateStatusFilter('closed'); + } else { + updateStatusFilter('none'); + } }; return ( <> -
+
Request Status 

@@ -49,40 +69,55 @@ function StatusSelector({

- - updateStatusFilter('open')} - value="open" - disableRipple - > - Open - - updateStatusFilter('all')} - value="all" - disableRipple - > - All - - updateStatusFilter('closed')} - value="closed" - disableRipple - > - Closed - - + + handleSelection('open', e.target.checked)} + /> + )} + label="Open" + /> + handleSelection('closed', e.target.checked)} + /> + )} + label="Closed" + /> + + ); } @@ -106,4 +141,5 @@ StatusSelector.propTypes = { open: PropTypes.bool.isRequired, closed: PropTypes.bool.isRequired, }).isRequired, + hasError: PropTypes.bool, }; diff --git a/src/components/layout/Main/Desktop/TypeSelector/index.jsx b/src/components/layout/Main/Desktop/TypeSelector/index.jsx index 3c2c6cad8..55717e9a5 100644 --- a/src/components/layout/Main/Desktop/TypeSelector/index.jsx +++ b/src/components/layout/Main/Desktop/TypeSelector/index.jsx @@ -40,6 +40,7 @@ function RequestTypeSelector({ requestTypes, dispatchUpdateTypesFilter, selectedTypes, + hasError = false // Form Validation for blank map }) { const [leftCol, setLeftCol] = useState(); const [rightCol, setRightCol] = useState(); @@ -80,7 +81,7 @@ function RequestTypeSelector({ return ( - + Request Types 

@@ -101,6 +102,7 @@ function RequestTypeSelector({ padding: '5px', paddingTop: '3px', paddingBottom: '3px', + border: hasError ? '1.3px solid #DE2800': undefined }} > {/* Request Types - Left Column */} diff --git a/src/features/Map/controls/MapSearch.jsx b/src/features/Map/controls/MapSearch.jsx index c294cefec..b3b4bccf7 100644 --- a/src/features/Map/controls/MapSearch.jsx +++ b/src/features/Map/controls/MapSearch.jsx @@ -129,8 +129,13 @@ class MapSearch extends React.Component { // This event fires upon an Address Search submission this.geocoder.on('result', ({ result }) => { - this.props.onGeocoderResult({ result }); + // this.props.onGeocoderResult({ result }); Temp: Do not trigger for blank map implementation + // Blank Map Implementation: pass result to FilterMenu + const selectedAddress = result.place_name; + console.log("MapSearch - Selected address:", selectedAddress); + this.props.onGeocoderResult(result); + // This clears the address from the Address input field. // this.geocoder.clear(); }); @@ -140,6 +145,13 @@ class MapSearch extends React.Component { // Add a custom event listener to clear the Address Search Input field this.addListener(geocoderElement, settings.map.eventName.reset, ()=>this.geocoder.clear() ) + + //Listens to event when search address bar is cleared + this.geocoder.on('clear', (result) => { + console.log("Clear event triggered"); + //Updates props.onGeocoderResult with an empty string and handles empty string value in FilterMenu for Blank Map Form Validation + this.props.onGeocoderResult({place_name: ''}); + }); // this.setTab(GEO_FILTER_TYPES.address); } @@ -152,7 +164,7 @@ class MapSearch extends React.Component { setTab = tab => { this.props.onChangeTab(tab); - this.geocoder.clear(); + // this.geocoder.clear(); this.geocoder.setPlaceholder(`Enter ${tab.toLowerCase()}`); switch(tab) { case GEO_FILTER_TYPES.address: @@ -184,8 +196,8 @@ class MapSearch extends React.Component { ))}

*/} - Search by Address -
+ Search by Address +
); } @@ -198,6 +210,7 @@ MapSearch.propTypes = { onChangeTab: PropTypes.func, onReset: PropTypes.func, canReset: PropTypes.bool, + hasError: PropTypes.bool, }; MapSearch.defaultProps = { @@ -206,7 +219,8 @@ MapSearch.defaultProps = { onGeocoderResult: () => {}, onChangeTab: () => {}, onReset: () => {}, - canReset: false + canReset: false, + hasError: false, }; export default withStyles(styles)(MapSearch); diff --git a/src/features/Map/index.jsx b/src/features/Map/index.jsx index 5c6549f29..911410d4c 100644 --- a/src/features/Map/index.jsx +++ b/src/features/Map/index.jsx @@ -16,6 +16,7 @@ import { } from '@reducers/filters'; import { updateMapPosition } from '@reducers/ui'; import { trackMapExport } from '@reducers/analytics'; +import { triggerDisplayData, resetDisplayData } from '@reducers/data'; import { INTERNAL_DATE_SPEC } from '../../components/common/CONSTANTS'; import { getTypeIdFromTypeName } from '@utils'; import LoadingModal from '../../components/Loading/LoadingModal'; @@ -91,12 +92,14 @@ class MapContainer extends React.Component { async componentDidMount(props) { this.isSubscribed = true; this.processSearchParams(); - await this.createRequestsTable(); - await this.setData(); + // Blank Map Implementation: createRequestTable and setData now called via Redux + // await this.createRequestsTable(); + // await this.setData(); + } async componentDidUpdate(prevProps) { - const { activeMode, startDate, endDate, councilId } = this.props; + const { activeMode, startDate, endDate, councilId, shouldDisplayData } = this.props; // create conditions to check if year or startDate or endDate changed const yearChanged = moment(prevProps.startDate).year() !== moment(startDate).year(); const startDateChanged = prevProps.startDate !== startDate; @@ -106,10 +109,18 @@ class MapContainer extends React.Component { // when both the startDate and endDate are selected. const didDateRangeChange = (yearChanged || startDateChanged || endDateChanged) && endDate !== null; - if (prevProps.activeMode !== activeMode || prevProps.councilId !== councilId || didDateRangeChange) { - await this.createRequestsTable(); - await this.setData(); - } + // if (prevProps.activeMode !== activeMode || prevProps.councilId !== councilId || didDateRangeChange) { + // await this.createRequestsTable(); + // await this.setData(); + // } + + // Trigger for Display Data button + if (shouldDisplayData && shouldDisplayData !== prevProps.shouldDisplayData) { + await this.createRequestsTable(); + await this.setData(); + // reset display data flag + this.props.dispatchResetDisplayData(); + } } async componentWillUnmount() { @@ -478,7 +489,8 @@ const mapStateToProps = (state) => ({ dateRangesWithRequests: state.data.dateRangesWithRequests, isMapLoading: state.data.isMapLoading, isDbLoading: state.data.isDbLoading, - councilId: state.filters.councilId, + councilId: state.filters.councilId, + shouldDisplayData: state.data.shouldDisplayData, }); const mapDispatchToProps = (dispatch) => ({ @@ -496,6 +508,8 @@ const mapDispatchToProps = (dispatch) => ({ dispatchUpdateEndDate: (endDate) => dispatch(updateEndDate(endDate)), dispatchUpdateNcId: (id) => dispatch(updateNcId(id)), dispatchUpdateTypesFilter: (type) => dispatch(updateRequestTypes(type)), + dispatchTriggerDisplayData: () => dispatch(triggerDisplayData()), + dispatchResetDisplayData: () => dispatch(resetDisplayData()), }); MapContainer.propTypes = {}; diff --git a/src/redux/reducers/data.js b/src/redux/reducers/data.js index 052d19aaa..9953d5c3f 100644 --- a/src/redux/reducers/data.js +++ b/src/redux/reducers/data.js @@ -25,6 +25,8 @@ export const types = { SEND_GIT_REQUEST: 'SEND_GIT_REQUEST', GIT_RESPONSE_SUCCESS: 'GIT_RESPONSE_SUCCESS', GIT_RESPONSE_FAILURE: 'GIT_RESPONSE_FAILURE', + TRIGGER_DISPLAY_DATA: 'TRIGGER_DISPLAY_DATA', + RESET_DISPLAY_DATA: 'RESET_DISPLAY_DATA' }; export const getDbRequest = () => ({ @@ -129,8 +131,16 @@ export const gitResponseFailure = error => ({ payload: error, }); +export const triggerDisplayData = () => ({ + type: types.TRIGGER_DISPLAY_DATA +}); + +export const resetDisplayData = () => ({ + type: types.RESET_DISPLAY_DATA +}); + const initialState = { - isDbLoading: true, + isDbLoading: false, isMapLoading: false, error: null, pins: [], @@ -139,6 +149,7 @@ const initialState = { // Empty GeoJSON object. requests: { type: 'FeatureCollection', features: [] }, dateRangesWithRequests: [], + shouldDisplayData: false, }; export default (state = initialState, action) => { @@ -262,6 +273,18 @@ export default (state = initialState, action) => { selectedNcId: action.payload, }; } + case types.TRIGGER_DISPLAY_DATA: { + return { + ...state, + shouldDisplayData: true + }; + } + case types.RESET_DISPLAY_DATA: { + return { + ...state, + shouldDisplayData: false + }; + } default: return state; } diff --git a/src/redux/reducers/filters.js b/src/redux/reducers/filters.js index 7ff004b79..c58949442 100644 --- a/src/redux/reducers/filters.js +++ b/src/redux/reducers/filters.js @@ -51,8 +51,8 @@ export const updateRequestStatus = status => ({ const initialState = { // dateRange: null, // Always store dates using the INTERNAL_DATE_SPEC. - startDate: moment(DATE_RANGES[0].startDate, USER_DATE_SPEC).format(INTERNAL_DATE_SPEC), - endDate: moment(DATE_RANGES[0].endDate, USER_DATE_SPEC).format(INTERNAL_DATE_SPEC), + startDate: null, + endDate: null, councilId: null, selected: [], unselected: [], @@ -177,6 +177,19 @@ export default (state = initialState, action) => { closed: true, }, }; + case 'none': + newSearchParams.set('requestStatusOpen', false); + newSearchParams.set('requestStatusClosed', false); + url.search = newSearchParams.toString(); + window.history.replaceState(null, 'Change URL', url); + return { + ...state, + requestStatus: { + ...state.requestStatus, + open: false, + closed: false, + }, + }; // default to non-exclusive v1 'open' and 'closed' toggle code // where 'open' and 'closed' can be selected/deselected at same time