diff --git a/src/containers/BuildingEvents/BuildingEventsTable.js b/src/containers/BuildingEvents/BuildingEventsTable.js index c87cdac8a91994e53850f24f8cbc44a09acd53b6..ba278892f09723658776fbec4b4769b9e6b49db5 100644 --- a/src/containers/BuildingEvents/BuildingEventsTable.js +++ b/src/containers/BuildingEvents/BuildingEventsTable.js @@ -4,17 +4,20 @@ import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { Card, Table, Row, Col, - ListGroup, ListGroupItem, Collapse, } from 'reactstrap'; import { Icon } from 'react-fa'; -import { loadEvents } from '../Event/actions'; +import { loadEvents, addSubscription, loadAlertSubscribers } from '../Event/actions'; import Loading from '../../components/Loading'; import ErrorAlert from '../../components/ErrorAlert'; class BuildingEventsTable extends Component { - state = { eventsOpen: false } + state = { + eventsOpen: false, + // The ARN for the email + emailSubArn: null, + } componentDidMount() { this.props.loadEvents({ @@ -22,54 +25,122 @@ class BuildingEventsTable extends Component { limit: this.props.limit, order: 'desc', }); + this.props.loadAlertSubscribers({ + 'building_id[]': this.props.buildingId, + }); } - renderHeading = (heading) => { - return ( - - -

{heading}

-
- -
- ); + componentWillReceiveProps(nextProps) { + if (nextProps === this.props) { + return; + } + /* eslint-disable no-param-reassign */ + if ( + nextProps.events.alertSubscribersData !== + this.props.events.alertSubscribersData + ) { + const topics = nextProps.events.alertSubscribersData; + const emailSubArn = topics.reduce( + (outerAcc, topic) => { + if (topic.building_id === nextProps.buildingId) { + return topic.subscriptions.reduce( + (innerAcc, subscription) => { + if ( + subscription.Protocol === 'email' && + subscription.Endpoint === localStorage.emailAddress + ) { + return subscription.SubscriptionArn; + } + return innerAcc; + }, outerAcc, + ); + } + return outerAcc; + }, null, + ); + this.setState({ emailSubArn }); + } + } - renderSpaces = (spaces) => { - return spaces.map(spc => ( - - {spc.description} {spc.floor ? ` - device on floor ${spc.floor}` : ''} - - )); + subscribeToAlert = () => { + this.props.addSubscription({ + building_id: this.props.buildingId, + sub_type: 'email', + sub_val: localStorage.emailAddress, + }); } - renderApartments = () => { - return this.props.events.map(apt => { + renderAlertSubscription = () => { + /* eslint-disable jsx-a11y/no-static-element-interactions */ + if (this.props.events.alertSubscribersLoading) { + return ( + + + + ); + } + if (this.props.events.alertSubscribersError) { + return ( + + + + ); + } + if (this.state.emailSubArn === 'PendingConfirmation' || this.state.emailSubArn === 'pending confirmation') { return ( -
  • + + {' '} + Pending Your Confirmation + + ); + } + if (this.state.emailSubArn === 'Deleted') { + return ( + -

    - Apartment {apt.number} -

    - - {this.renderSpaces(apt.spaces)} - -
  • + + {' '} + Resubscribe to alerts + ); - }); - } - - renderArea = (incArea) => { - return incArea.map(area => { + } + if (this.state.emailSubArn !== null) { return ( - - {this.renderSpaces(area.spaces)} - + + + {' '} + Subscribed + ); - }); + } + + return ( + + + {' '} + Subscribe to alerts + + ); } render() { @@ -107,18 +178,26 @@ class BuildingEventsTable extends Component { } else if (this.props.events.eventsData.length === 0) { mainContent = 'Sorry, no events data for this building'; } - /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */ + /* eslint-disable jsx-a11y/no-static-element-interactions */ return (
    -

    ( - this.setState({ eventsOpen: !this.state.eventsOpen }) - )} - style={{ cursor: 'pointer' }} - > - Recent Sensor Events {' '} - +

    + + ( + this.setState({ eventsOpen: !this.state.eventsOpen }) + )} + style={{ cursor: 'pointer' }} + lg="8" + md="6" + sm="12" + > + Recent Sensor Events {' '} + + + {this.renderAlertSubscription()} +

    {mainContent} @@ -132,6 +211,8 @@ class BuildingEventsTable extends Component { BuildingEventsTable.propTypes = { buildingId: PropTypes.string, loadEvents: PropTypes.func, + addSubscription: PropTypes.func, + loadAlertSubscribers: PropTypes.func, events: PropTypes.object, // eslint-disable-line limit: PropTypes.number, }; @@ -147,6 +228,8 @@ const mapStateToProps = state => ({ const mapDispatchToProps = dispatch => ( bindActionCreators({ loadEvents, + loadAlertSubscribers, + addSubscription, }, dispatch) ); diff --git a/src/containers/Event/actions.js b/src/containers/Event/actions.js index 07b2c1ad75ccca7bd5550e017f1ebbea7e5fa72a..2d2604926f4c46c20a205d198ab3a1aa5752515c 100644 --- a/src/containers/Event/actions.js +++ b/src/containers/Event/actions.js @@ -2,6 +2,12 @@ import { EVENTS_REQUESTED, EVENTS_SUCCEEDED, EVENTS_FAILED, + ALERT_SUBSCRIBERS_REQUESTED, + ALERT_SUBSCRIBERS_SUCCEEDED, + ALERT_SUBSCRIBERS_FAILED, + ADD_SUBSCRIPTION_REQUESTED, + ADD_SUBSCRIPTION_SUCCEEDED, + ADD_SUBSCRIPTION_FAILED, } from './constants'; import { makeActionCreator } from '../../utils/reduxHelpers'; @@ -11,3 +17,10 @@ export const loadEvents = makeActionCreator(EVENTS_REQUESTED, 'filters'); export const eventsLoaded = makeActionCreator(EVENTS_SUCCEEDED, 'eventsData'); export const eventsFailed = makeActionCreator(EVENTS_FAILED, 'error'); +export const loadAlertSubscribers = makeActionCreator(ALERT_SUBSCRIBERS_REQUESTED, 'filters'); +export const alertSubscribersLoaded = makeActionCreator(ALERT_SUBSCRIBERS_SUCCEEDED, 'alertSubscribersData'); +export const alertSubscribersFailed = makeActionCreator(ALERT_SUBSCRIBERS_FAILED, 'error'); + +export const addSubscription = makeActionCreator(ADD_SUBSCRIPTION_REQUESTED, 'payload'); +export const subscriptionAdded = makeActionCreator(ADD_SUBSCRIPTION_SUCCEEDED, 'subscription'); +export const subscriptionAddFailed = makeActionCreator(ADD_SUBSCRIPTION_FAILED, 'error'); diff --git a/src/containers/Event/constants.js b/src/containers/Event/constants.js index cd55049a82f6032117c27b5ce3f09d32f618a9b9..26d960ee3633c21fcab36975a8423eae267bdf3a 100644 --- a/src/containers/Event/constants.js +++ b/src/containers/Event/constants.js @@ -1,4 +1,11 @@ -/* Retrieve Events */ export const EVENTS_REQUESTED = 'EVENTS_REQUESTED'; export const EVENTS_SUCCEEDED = 'EVENTS_SUCCEEDED'; export const EVENTS_FAILED = 'EVENTS_FAILED'; + +export const ALERT_SUBSCRIBERS_REQUESTED = 'ALERT_SUBSCRIBERS_REQUESTED'; +export const ALERT_SUBSCRIBERS_SUCCEEDED = 'ALERT_SUBSCRIBERS_SUCCEEDED'; +export const ALERT_SUBSCRIBERS_FAILED = 'ALERT_SUBSCRIBERS_FAILED'; + +export const ADD_SUBSCRIPTION_REQUESTED = 'ADD_SUBSCRIPTION_REQUESTED'; +export const ADD_SUBSCRIPTION_SUCCEEDED = 'ADD_SUBSCRIPTION_SUCCEEDED'; +export const ADD_SUBSCRIPTION_FAILED = 'ADD_SUBSCRIPTION_FAILED'; diff --git a/src/containers/Event/reducer.js b/src/containers/Event/reducer.js index 567de9e77a925603cd2f71e155cd6370db66d1fc..136f7b8c8d17d1703f174162356ffbc3372931a0 100644 --- a/src/containers/Event/reducer.js +++ b/src/containers/Event/reducer.js @@ -2,12 +2,21 @@ import { EVENTS_REQUESTED, EVENTS_SUCCEEDED, EVENTS_FAILED, + ALERT_SUBSCRIBERS_REQUESTED, + ALERT_SUBSCRIBERS_SUCCEEDED, + ALERT_SUBSCRIBERS_FAILED, + ADD_SUBSCRIPTION_REQUESTED, + ADD_SUBSCRIPTION_SUCCEEDED, + ADD_SUBSCRIPTION_FAILED, } from './constants'; export const initState = { eventsLoading: false, eventsError: false, eventsData: [], + alertSubscribersLoading: false, + alertSubscribersError: false, + alertSubscribersData: [], }; export default (state = initState, action) => { @@ -26,6 +35,55 @@ export default (state = initState, action) => { case EVENTS_FAILED: return { ...state, eventsLoading: false, eventsError: action.error }; + case ALERT_SUBSCRIBERS_REQUESTED: + return { ...state, alertSubscribersLoading: true, alertSubscribersError: false }; + + case ALERT_SUBSCRIBERS_SUCCEEDED: + return { + ...state, + alertSubscribersLoading: false, + alertSubscribersError: false, + alertSubscribersData: action.alertSubscribersData.data, + }; + + case ALERT_SUBSCRIBERS_FAILED: + return { ...state, alertSubscribersLoading: false, alertSubscribersError: action.error }; + + case ADD_SUBSCRIPTION_REQUESTED: + return { ...state, alertSubscribersLoading: true, alertSubscribersError: false }; + + /* eslint-disable no-param-reassign */ + case ADD_SUBSCRIPTION_SUCCEEDED: + return { + ...state, + alertSubscribersLoading: false, + alertSubscribersError: false, + alertSubscribersData: state.alertSubscribersData.map((topic) => { + if (topic.arn === action.subscription.data.topic_arn) { + let updated = false; + topic.subscriptions.map((subscriber) => { + if (subscriber.Endpoint === action.subscription.data.sub_val) { + subscriber.SubscriptionArn = action.subscription.data.subscription_arn; + updated = true; + } + return subscriber; + }); + if (!updated) { + topic.subscriptions.push({ + Endpoint: action.subscription.data.sub_val, + Protocol: action.subscription.data.sub_type, + SubscriptionArn: action.subscription.data.subscription_arn, + TopicArn: action.subscription.data.topic_arn, + }); + } + } + return topic; + }), + }; + + case ADD_SUBSCRIPTION_FAILED: + return { ...state, alertSubscribersLoading: false, alertSubscribersError: action.error }; + default: return state; } diff --git a/src/containers/Event/sagas.js b/src/containers/Event/sagas.js index bcb5b9a794d017e5c76efc2d3327496fb6633c44..52f8e82d5d57583a6fdbb6a5ec1fcae74c91533e 100644 --- a/src/containers/Event/sagas.js +++ b/src/containers/Event/sagas.js @@ -1,23 +1,49 @@ import { takeEvery } from 'redux-saga/effects'; import SagaRequests from '../../utils/sagaRequests'; -import { eventsURL } from '../../utils/restServices'; +import { eventsURL, alertSubscribersURL } from '../../utils/restServices'; import { EVENTS_REQUESTED, + ALERT_SUBSCRIBERS_REQUESTED, + ADD_SUBSCRIPTION_REQUESTED, } from './constants'; import { eventsLoaded, eventsFailed, + alertSubscribersLoaded, + alertSubscribersFailed, + subscriptionAdded, + subscriptionAddFailed, } from './actions'; function* getEvents(action) { yield SagaRequests.get(action, eventsURL, eventsLoaded, eventsFailed); } +function* getAlertSubscribers(action) { + yield SagaRequests.get( + action, + alertSubscribersURL, + alertSubscribersLoaded, + alertSubscribersFailed, + ); +} + +function* addSubscription(action) { + yield SagaRequests.post( + action, + alertSubscribersURL, + subscriptionAdded, + subscriptionAddFailed, + ); +} + function* eventsWatcher() { yield takeEvery(EVENTS_REQUESTED, getEvents); + yield takeEvery(ALERT_SUBSCRIBERS_REQUESTED, getAlertSubscribers); + yield takeEvery(ADD_SUBSCRIPTION_REQUESTED, addSubscription); } export default eventsWatcher; diff --git a/src/containers/Sensors/SensorGraph.js b/src/containers/Sensors/SensorGraph.js index 3f74b8e1c45ed0d18a11214875db5165852ad68e..1a0c05344513c65c2a3392d21633afef8938540e 100644 --- a/src/containers/Sensors/SensorGraph.js +++ b/src/containers/Sensors/SensorGraph.js @@ -82,6 +82,7 @@ export class SensorGraph extends Component { ) generateData = (props) => { + this.setState({ firstLoad: false }); if (this.sensorDataLoading(props)) { return; } @@ -684,14 +685,12 @@ export class SensorGraph extends Component {

    Sensor Data

    -

    - From - -

    + From + To diff --git a/src/utils/restServices.js b/src/utils/restServices.js index c7158fbf8d8dac5c91cdd97bf28b300c6421d4a7..3e763e1e41d8562ea92091575c2e8c6e371ea4ff 100644 --- a/src/utils/restServices.js +++ b/src/utils/restServices.js @@ -48,6 +48,7 @@ export const gatewayURL = `${iotService}/gateway/`; export const sensewareNodeURL = `${iotService}/sensewarenode/`; export const sensorImageURL = `${iotService}/sensorimage/`; export const eventsURL = `${iotService}/event/`; +export const alertSubscribersURL = `${iotService}/alertsubscription/`; export const userURL = `${userService}/user/`; export const userGroupsURL = `${userService}/usergroup/`;