From 2c8d6bd0770eef76b2434613e146c29a10eaec36 Mon Sep 17 00:00:00 2001 From: Bilal Catic Date: Sun, 16 Jun 2019 11:53:20 +0200 Subject: [PATCH 1/8] add controller and service to fetch all incidents --- controllers/integration.js | 23 ++++++++ routes/index.js | 6 +- services/integration/reports.js | 98 +++++++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 services/integration/reports.js diff --git a/controllers/integration.js b/controllers/integration.js index 6d56b8c..ddaf1cf 100644 --- a/controllers/integration.js +++ b/controllers/integration.js @@ -1,6 +1,7 @@ 'use strict'; const { getMappingsFromDatabase, fetchOffices, fetchResources, saveNewMappingToDatabase } = require('../services/officeRnD/resources'); +const { getAllDoorLockIncidents } = require('../services/integration/reports'); const getKnownOfficeResourceMappings = (req, res) => { const dataToFetch = [getMappingsFromDatabase(), fetchOffices(), fetchResources() ]; @@ -31,7 +32,29 @@ const addNewMapping = (req, res) => { } }; +const getAllIncidents = (req, res) => { + getAllDoorLockIncidents() + .then((incidents) => { + res.send(incidents); + }) + .catch((error) => { + console.log(error); + res.send([]); + }); +}; + +const getUnlockedIncidents = (req, res) => { + +}; + +const getUnscheduledIncidents = (req, res) => { + +}; + module.exports = { getKnownOfficeResourceMappings, addNewMapping, + getAllIncidents, + getUnscheduledIncidents, + getUnlockedIncidents, }; diff --git a/routes/index.js b/routes/index.js index 6e007a2..c1b82eb 100644 --- a/routes/index.js +++ b/routes/index.js @@ -2,7 +2,7 @@ const { apiStatusCheck } = require('../controllers/apiStatusCheck'); const { uploadDoorLockData } = require('../controllers/doorLock'); -const { getKnownOfficeResourceMappings, addNewMapping } = require('../controllers/integration'); +const { getKnownOfficeResourceMappings, addNewMapping, getAllIncidents,getUnscheduledIncidents, getUnlockedIncidents } = require('../controllers/integration'); const { calculateDoorLockCharges } = require('../services/integration/doorLockCharges'); const express = require('express'); @@ -14,6 +14,10 @@ router.post('/doorLock/upload', uploadDoorLockData); router.get('/integration/mappings', getKnownOfficeResourceMappings); router.post('/integration/mappings', addNewMapping); +router.get('/integration/report/allIncidents', getAllIncidents); +router.get('/integration/report/unlockedIncidents', getUnlockedIncidents); +router.get('/integration/report/unscheduledIncidents', getUnscheduledIncidents); + // temporary route, manually trigger door lock charge calculations router.get('/calculate', (req, res) => { calculateDoorLockCharges(); res.send();}); diff --git a/services/integration/reports.js b/services/integration/reports.js new file mode 100644 index 0000000..ba2200a --- /dev/null +++ b/services/integration/reports.js @@ -0,0 +1,98 @@ +'use strict'; + +const db = require('../../models/index'); +const { incidentType } = require('../../constants/constants'); + +const { fetchAllMembers } = require('../officeRnD/members'); + +const getUnlockedIncidents = () => { + const attributes = ['id', 'memberId', 'resourceId', 'bookingStart', 'bookingEnd', 'incidentLevel', 'incidentLevelPrice']; + + return db.unlockedIncident.findAll({ + attributes, + sort: [ + ['bookingStart', 'ASC'] + ] + }); +}; + +const getUnscheduledIncidents = () => { + const attributes = [ + 'id', + 'memberId', + 'resourceId', + 'bookingStart', + 'bookingEnd', + 'doorLockEventTimestamp', + 'doorLockEventType', + 'timeIntervalsToCharge', + 'chargePrice', + 'totalChargeFee' + ]; + + return db.unscheduledIncident.findAll({ + attributes, + sort: [ + ['bookingStart', 'ASC'] + ] + }); +}; + +const getAllDoorLockIncidents = () => { + return new Promise ((resolve, reject) => { + const dataFetchJobs = [fetchAllMembers(), getUnlockedIncidents(), getUnscheduledIncidents()]; + + Promise.all(dataFetchJobs) + .then((data) => { + const members = data[0]; + const unlockedIncidents = data[1]; + const unscheduledIncidents = data[2]; + + const membersMap = {}; + + members.forEach((member) => membersMap[member.memberId] = member); + + const allIncidents = []; + + unlockedIncidents.forEach((unlockedIncident) => { + allIncidents.push({ + incidentId: unlockedIncident.id, + memberId: unlockedIncident.memberId, + memberName: membersMap[unlockedIncident.memberId].name, + resourceName: '', + officeName: '', + bookingStart: unlockedIncident.bookingStart, + bookingEnd: unlockedIncident.bookingEnd, + incidentType: incidentType.UNLOCKED_INCIDENT, + incidentLevel: unlockedIncident.incidentLevel, + incidentPrice: unlockedIncident.incidentLevelPrice, + }); + }); + + unscheduledIncidents.forEach((unscheduledIncident) => { + allIncidents.push({ + incidentId: unscheduledIncident.id, + memberId: unscheduledIncident.memberId, + memberName: membersMap[unscheduledIncident.memberId].name, + resourceName: '', + officeName: '', + bookingStart: unscheduledIncident.bookingStart, + bookingEnd: unscheduledIncident.bookingEnd, + incidentType: incidentType.UNSCHEDULED_INCIDENT, + timeIntervalsToCharge: unscheduledIncident.timeIntervalsToCharge, + chargePrice: unscheduledIncident.chargePrice, + totalChargeFee: unscheduledIncident.totalChargeFee, + }); + }); + + resolve(allIncidents); + }) + .catch((error) => reject(error)); + }); +}; + +module.exports = { + getUnlockedIncidents, + getUnscheduledIncidents, + getAllDoorLockIncidents, +}; From 4c4e45f89f1e1b8f602488a90ae193b0712b9b2d Mon Sep 17 00:00:00 2001 From: Bilal Catic Date: Sun, 16 Jun 2019 12:18:48 +0200 Subject: [PATCH 2/8] fix code style --- client/src/components/MainMenu/index.js | 2 +- client/src/scenes/Home/index.js | 2 +- client/src/scenes/NotFound/index.js | 2 +- client/src/scenes/UploadDLockData/components/FileUpload.js | 4 ++-- .../src/scenes/UploadDLockData/components/UnknownMapping.js | 4 ++-- client/src/scenes/UploadDLockData/index.js | 2 +- client/src/store/actions/doorLockActions.js | 2 +- client/src/store/reducers/addMappingReducer.js | 2 +- client/src/store/reducers/doorLockReducers.js | 2 +- client/src/store/reducers/mappingsReducer.js | 2 +- 10 files changed, 12 insertions(+), 12 deletions(-) diff --git a/client/src/components/MainMenu/index.js b/client/src/components/MainMenu/index.js index 8581220..6890a4c 100644 --- a/client/src/components/MainMenu/index.js +++ b/client/src/components/MainMenu/index.js @@ -2,7 +2,7 @@ import React from 'react'; import { NavLink } from 'react-router-dom'; import { Menu } from 'semantic-ui-react'; -import { mainMenuItems } from "../../constants/menuItems"; +import { mainMenuItems } from '../../constants/menuItems'; const MainMenu = () => ( diff --git a/client/src/scenes/Home/index.js b/client/src/scenes/Home/index.js index 6f77d40..599ec3b 100644 --- a/client/src/scenes/Home/index.js +++ b/client/src/scenes/Home/index.js @@ -1,6 +1,6 @@ import React, { Component } from 'react'; -import { Container, Form } from "semantic-ui-react"; +import { Container, Form } from 'semantic-ui-react'; import MainMenu from '../../components/MainMenu'; diff --git a/client/src/scenes/NotFound/index.js b/client/src/scenes/NotFound/index.js index f63157a..300277a 100644 --- a/client/src/scenes/NotFound/index.js +++ b/client/src/scenes/NotFound/index.js @@ -1,5 +1,5 @@ import React from 'react'; -import MainMenu from "../../components/MainMenu"; +import MainMenu from '../../components/MainMenu'; export default function NotFound () { return ( diff --git a/client/src/scenes/UploadDLockData/components/FileUpload.js b/client/src/scenes/UploadDLockData/components/FileUpload.js index 87b1038..e0f9b23 100644 --- a/client/src/scenes/UploadDLockData/components/FileUpload.js +++ b/client/src/scenes/UploadDLockData/components/FileUpload.js @@ -1,10 +1,10 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; -import { Form } from "semantic-ui-react"; +import { Form } from 'semantic-ui-react'; import UnknownMapping from './UnknownMapping'; -import { uploadDoorLockData, fetchMappings } from "../../../store/actions"; +import { uploadDoorLockData, fetchMappings } from '../../../store/actions'; class FileUpload extends Component { constructor(props) { diff --git a/client/src/scenes/UploadDLockData/components/UnknownMapping.js b/client/src/scenes/UploadDLockData/components/UnknownMapping.js index 3bee93e..32846e8 100644 --- a/client/src/scenes/UploadDLockData/components/UnknownMapping.js +++ b/client/src/scenes/UploadDLockData/components/UnknownMapping.js @@ -1,8 +1,8 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; -import {Button, Dropdown, Message} from "semantic-ui-react"; +import {Button, Dropdown, Message} from 'semantic-ui-react'; import Fuse from 'fuse.js'; -import { addNewMapping } from "../../../store/actions"; +import { addNewMapping } from '../../../store/actions'; class UnknownMapping extends Component { constructor(props) { diff --git a/client/src/scenes/UploadDLockData/index.js b/client/src/scenes/UploadDLockData/index.js index b2e4e52..8d245c8 100644 --- a/client/src/scenes/UploadDLockData/index.js +++ b/client/src/scenes/UploadDLockData/index.js @@ -1,5 +1,5 @@ import React from 'react'; -import { Container, Form } from "semantic-ui-react"; +import { Container, Form } from 'semantic-ui-react'; import MainMenu from '../../components/MainMenu'; import FileUpload from './components/FileUpload'; diff --git a/client/src/store/actions/doorLockActions.js b/client/src/store/actions/doorLockActions.js index a5c5a01..47b296e 100644 --- a/client/src/store/actions/doorLockActions.js +++ b/client/src/store/actions/doorLockActions.js @@ -2,7 +2,7 @@ import { UPLOAD_DOOR_LOCK_DATA_PENDING, UPLOAD_DOOR_LOCK_DATA_SUCCESS, UPLOAD_DOOR_LOCK_DATA_FAILED -} from "../constants"; +} from '../constants'; import API from '../../utilities/api'; diff --git a/client/src/store/reducers/addMappingReducer.js b/client/src/store/reducers/addMappingReducer.js index c360882..ae7d7a8 100644 --- a/client/src/store/reducers/addMappingReducer.js +++ b/client/src/store/reducers/addMappingReducer.js @@ -2,7 +2,7 @@ import { ADD_NEW_MAPPING_PENDING, ADD_NEW_MAPPING_SUCCESS, ADD_NEW_MAPPING_FAILED, -} from "../constants"; +} from '../constants'; const initialState = { pending: false, diff --git a/client/src/store/reducers/doorLockReducers.js b/client/src/store/reducers/doorLockReducers.js index 97158d3..047a631 100644 --- a/client/src/store/reducers/doorLockReducers.js +++ b/client/src/store/reducers/doorLockReducers.js @@ -2,7 +2,7 @@ import { UPLOAD_DOOR_LOCK_DATA_PENDING, UPLOAD_DOOR_LOCK_DATA_SUCCESS, UPLOAD_DOOR_LOCK_DATA_FAILED -} from "../constants"; +} from '../constants'; const initialState = { pending: false, diff --git a/client/src/store/reducers/mappingsReducer.js b/client/src/store/reducers/mappingsReducer.js index 95e0d9d..156abee 100644 --- a/client/src/store/reducers/mappingsReducer.js +++ b/client/src/store/reducers/mappingsReducer.js @@ -2,7 +2,7 @@ import { FETCH_MAPPINGS_PENDING, FETCH_MAPPINGS_SUCCESS, FETCH_MAPPINGS_FAILED, -} from "../constants"; +} from '../constants'; const initialState = { pending: false, From 3090b0c0cb1da6a97e84880737b15d9af1308ee6 Mon Sep 17 00:00:00 2001 From: Bilal Catic Date: Mon, 17 Jun 2019 12:41:55 +0200 Subject: [PATCH 3/8] add timezone env variable --- environment.env | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/environment.env b/environment.env index 4283c09..6d88ba9 100644 --- a/environment.env +++ b/environment.env @@ -3,7 +3,9 @@ BASIC_AUTH_PASSWORD=password OFFICE_RnD_TOKEN=token for Office RnD API requests MAX_BACK_TO_BACK_DIFFERENCE=Time in minutes -EARLIEST_UNLOCK=2 +EARLIEST_UNLOCK=Time in minutes + +UI_TIMEZONE=Timezone for user interface | https://en.wikipedia.org/wiki/List_of_tz_database_time_zones | example : America/Los_Angeles UNSCHEDULED_USE_TIME_RESOLUTION=Time in minutes UNSCHEDULED_USE_CHARGE_FEE=Charge fee From cd50e1e67142be9c8b74f2be1ef31eb696773bab Mon Sep 17 00:00:00 2001 From: Bilal Catic Date: Mon, 17 Jun 2019 12:42:17 +0200 Subject: [PATCH 4/8] add react table package --- client/package.json | 1 + client/yarn.lock | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/client/package.json b/client/package.json index 16941b2..26300b5 100644 --- a/client/package.json +++ b/client/package.json @@ -10,6 +10,7 @@ "react-redux": "^7.0.3", "react-router-dom": "^5.0.0", "react-scripts": "3.0.1", + "react-table": "^6.10.0", "redux": "^4.0.1", "redux-thunk": "^2.3.0", "semantic-ui-css": "^2.4.1", diff --git a/client/yarn.lock b/client/yarn.lock index a426655..dd8a44d 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -2140,7 +2140,7 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -classnames@^2.2.6: +classnames@^2.2.5, classnames@^2.2.6: version "2.2.6" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" @@ -7095,6 +7095,12 @@ react-scripts@3.0.1: optionalDependencies: fsevents "2.0.6" +react-table@^6.10.0: + version "6.10.0" + resolved "https://registry.yarnpkg.com/react-table/-/react-table-6.10.0.tgz#20444b19d8ca3c1a08e7544e5c3a93e4ba56690e" + dependencies: + classnames "^2.2.5" + react@^16.8.6: version "16.8.6" resolved "https://registry.yarnpkg.com/react/-/react-16.8.6.tgz#ad6c3a9614fd3a4e9ef51117f54d888da01f2bbe" From f9bd84c9c5b742d041e68ef4af81df1f646ae4af Mon Sep 17 00:00:00 2001 From: Bilal Catic Date: Mon, 17 Jun 2019 12:44:57 +0200 Subject: [PATCH 5/8] improve report data sent from backend --- services/integration/reports.js | 36 +++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/services/integration/reports.js b/services/integration/reports.js index ba2200a..ce54691 100644 --- a/services/integration/reports.js +++ b/services/integration/reports.js @@ -1,9 +1,12 @@ 'use strict'; +const moment = require('moment-timezone'); + const db = require('../../models/index'); const { incidentType } = require('../../constants/constants'); const { fetchAllMembers } = require('../officeRnD/members'); +const { fetchOffices, fetchResources } = require('../officeRnD/resources'); const getUnlockedIncidents = () => { const attributes = ['id', 'memberId', 'resourceId', 'bookingStart', 'bookingEnd', 'incidentLevel', 'incidentLevelPrice']; @@ -38,19 +41,30 @@ const getUnscheduledIncidents = () => { }); }; +const formatTime = (timestamp) => { + const timezone = process.env.UI_TIMEZONE || 'America/Los_Angeles'; + return moment.tz(timestamp, timezone).format('MM/DD/YYYY hh:mm a'); +}; + const getAllDoorLockIncidents = () => { return new Promise ((resolve, reject) => { - const dataFetchJobs = [fetchAllMembers(), getUnlockedIncidents(), getUnscheduledIncidents()]; + const dataFetchJobs = [fetchAllMembers(), fetchOffices(), fetchResources(), getUnlockedIncidents(), getUnscheduledIncidents()]; Promise.all(dataFetchJobs) .then((data) => { const members = data[0]; - const unlockedIncidents = data[1]; - const unscheduledIncidents = data[2]; + const offices = data[1]; + const resources = data[2]; + const unlockedIncidents = data[3]; + const unscheduledIncidents = data[4]; const membersMap = {}; + const officesMap = {}; + const resourcesMap = {}; members.forEach((member) => membersMap[member.memberId] = member); + offices.forEach((office) => officesMap[office.officeId] = office); + resources.forEach((resource) => resourcesMap[resource.resourceId] = resource); const allIncidents = []; @@ -59,10 +73,10 @@ const getAllDoorLockIncidents = () => { incidentId: unlockedIncident.id, memberId: unlockedIncident.memberId, memberName: membersMap[unlockedIncident.memberId].name, - resourceName: '', - officeName: '', - bookingStart: unlockedIncident.bookingStart, - bookingEnd: unlockedIncident.bookingEnd, + resourceName: resourcesMap[unlockedIncident.resourceId].resourceName, + officeName: officesMap[resourcesMap[unlockedIncident.resourceId].officeId].officeName, + bookingStart: formatTime(unlockedIncident.bookingStart), + bookingEnd: formatTime(unlockedIncident.bookingEnd), incidentType: incidentType.UNLOCKED_INCIDENT, incidentLevel: unlockedIncident.incidentLevel, incidentPrice: unlockedIncident.incidentLevelPrice, @@ -74,10 +88,10 @@ const getAllDoorLockIncidents = () => { incidentId: unscheduledIncident.id, memberId: unscheduledIncident.memberId, memberName: membersMap[unscheduledIncident.memberId].name, - resourceName: '', - officeName: '', - bookingStart: unscheduledIncident.bookingStart, - bookingEnd: unscheduledIncident.bookingEnd, + resourceName: resourcesMap[unscheduledIncident.resourceId].resourceName, + officeName: officesMap[resourcesMap[unscheduledIncident.resourceId].officeId].officeName, + bookingStart: formatTime(unscheduledIncident.bookingStart), + bookingEnd: formatTime(unscheduledIncident.bookingEnd), incidentType: incidentType.UNSCHEDULED_INCIDENT, timeIntervalsToCharge: unscheduledIncident.timeIntervalsToCharge, chargePrice: unscheduledIncident.chargePrice, From 781943026a8ca9082fc58956dc7e3af8608be7c4 Mon Sep 17 00:00:00 2001 From: Bilal Catic Date: Mon, 17 Jun 2019 12:45:23 +0200 Subject: [PATCH 6/8] fetch and display door lock incidents on frontend --- client/src/constants/enums.js | 17 +++ client/src/constants/menuItems.js | 22 +++- client/src/scenes/IncidentsReport/index.js | 100 ++++++++++++++++++ .../src/store/actions/integrationActions.js | 16 ++- client/src/store/constants.js | 4 + .../store/reducers/incidentsReportReducer.js | 38 +++++++ client/src/store/reducers/index.js | 8 +- 7 files changed, 199 insertions(+), 6 deletions(-) create mode 100644 client/src/constants/enums.js create mode 100644 client/src/scenes/IncidentsReport/index.js create mode 100644 client/src/store/reducers/incidentsReportReducer.js diff --git a/client/src/constants/enums.js b/client/src/constants/enums.js new file mode 100644 index 0000000..a6da2d0 --- /dev/null +++ b/client/src/constants/enums.js @@ -0,0 +1,17 @@ + +export const UNLOCKED_INCIDENT = 2; +export const UNSCHEDULED_INCIDENT = 3; + +export const incidentDescriptions = {}; +incidentDescriptions[UNLOCKED_INCIDENT] = 'User left door unlocked'; +incidentDescriptions[UNSCHEDULED_INCIDENT] = 'Unscheduled use'; + +export const incidentLevelDescriptions = { + UNLOCKED_0: 'First month', + UNLOCKED_1: 'Second month', + UNLOCKED_2: 'Third month', + UNLOCKED_3: 'Fourth month', + UNLOCKED_4: 'Fifth month', + UNLOCKED_5: 'Sixth month', + +}; diff --git a/client/src/constants/menuItems.js b/client/src/constants/menuItems.js index cd4f3d2..8a910e3 100644 --- a/client/src/constants/menuItems.js +++ b/client/src/constants/menuItems.js @@ -1,5 +1,6 @@ -import UploadDLockData from "../scenes/UploadDLockData"; -import Home from "../scenes/Home"; +import UploadDLockData from '../scenes/UploadDLockData'; +import Home from '../scenes/Home'; +import IncidentsReport from '../scenes/IncidentsReport'; export const mainMenuItems = [ { @@ -8,6 +9,12 @@ export const mainMenuItems = [ url: '/', component: Home, }, + { + id: 'report', + title: 'Incidents Report', + url: '/incidents-report', + component: IncidentsReport, + }, { id: 'uploadDLockData', title: 'DLock', @@ -15,3 +22,14 @@ export const mainMenuItems = [ component: UploadDLockData, }, ]; + +export const incidentsReportHeaderTitles = { + officeName: 'Office', + resourceName: 'Room', + bookingStart: 'Reservation Start', + bookingEnd: 'Reservation End', + memberName: 'Member Name', + incidentType: 'Incident Type', + feeDescription: 'Fee description', + totalChargeFee: 'Total Fee', +}; diff --git a/client/src/scenes/IncidentsReport/index.js b/client/src/scenes/IncidentsReport/index.js new file mode 100644 index 0000000..defc361 --- /dev/null +++ b/client/src/scenes/IncidentsReport/index.js @@ -0,0 +1,100 @@ +import React, { Component } from 'react'; +import { connect } from 'react-redux'; +import { Container, Loader } from 'semantic-ui-react'; +import ReactTable from 'react-table'; +import 'react-table/react-table.css'; + +import MainMenu from '../../components/MainMenu'; +import { fetchIncidents } from '../../store/actions'; +import { incidentsReportHeaderTitles } from '../../constants/menuItems'; +import { incidentDescriptions, incidentLevelDescriptions, UNSCHEDULED_INCIDENT, UNLOCKED_INCIDENT } from '../../constants/enums'; + +class IncidentsReport extends Component { + + componentDidMount() { + const { fetchIncidents } = this.props; + fetchIncidents(); + } + + render () { + const { pendingIncidents, incidents } = this.props; + + const columns = []; + if (incidents && incidents.length > 0){ + const incidentHeaders = Object.keys(incidentsReportHeaderTitles); + + incidentHeaders.forEach((header) => { + const columnTitle = incidentsReportHeaderTitles[header]; + + if (columnTitle){ + columns.push({ + Header: incidentsReportHeaderTitles[header], + accessor: header, + Cell: props => { + let cellValue; + + switch (props.column.id) { + case 'incidentType': + cellValue = incidentDescriptions[props.value]; + break; + case 'incidentLevel': + cellValue = incidentLevelDescriptions[props.value]; + break; + + case 'feeDescription': + const { incidentType, incidentLevel, timeIntervalsToCharge } = props.row['_original']; + switch (incidentType) { + case UNLOCKED_INCIDENT: + cellValue = `${incidentLevelDescriptions[incidentLevel]}`; + break; + case UNSCHEDULED_INCIDENT: + cellValue = `${timeIntervalsToCharge} x 5 min`; + break; + } + break; + + case 'totalChargeFee': + const totalFee = props.value ? props.value : props.row['_original'].incidentPrice; + cellValue = `$ ${totalFee}`; + break; + + default: + cellValue = props.value; + } + + return {cellValue} + } + }); + } + }); + } + + return ( + + +

Incidents Report

+
+ + { + !pendingIncidents && incidents && + + } +
+ ); + } +} + +const mapStateToProps = (state) => ({ + pendingIncidents: state.incidentsReport.pending, + incidents: state.incidentsReport.result, +}); + +const mapDispatchToProps = (dispatch) => ({ + fetchIncidents: () => fetchIncidents(dispatch), +}); + +export default connect(mapStateToProps, mapDispatchToProps)(IncidentsReport); diff --git a/client/src/store/actions/integrationActions.js b/client/src/store/actions/integrationActions.js index b61b58b..599573f 100644 --- a/client/src/store/actions/integrationActions.js +++ b/client/src/store/actions/integrationActions.js @@ -5,7 +5,10 @@ import { ADD_NEW_MAPPING_PENDING, ADD_NEW_MAPPING_SUCCESS, ADD_NEW_MAPPING_FAILED, -} from "../constants"; + FETCH_INCIDENTS_PENDING, + FETCH_INCIDENTS_SUCCESS, + FETCH_INCIDENTS_FAILED, +} from '../constants'; import API from '../../utilities/api'; @@ -32,3 +35,14 @@ export const addNewMapping = (dispatch, mapping) => { dispatch({type: ADD_NEW_MAPPING_FAILED, payload: error.response}); }); }; + +export const fetchIncidents = (dispatch) => { + dispatch({type: FETCH_INCIDENTS_PENDING}); + API.get('integration/report/allIncidents') + .then(response => { + dispatch({type: FETCH_INCIDENTS_SUCCESS, payload: response.data}); + }) + .catch(error => { + dispatch({type: FETCH_INCIDENTS_FAILED, payload: error.response}); + }); +}; diff --git a/client/src/store/constants.js b/client/src/store/constants.js index aecc675..9b20bbc 100644 --- a/client/src/store/constants.js +++ b/client/src/store/constants.js @@ -9,3 +9,7 @@ export const FETCH_MAPPINGS_FAILED = 'FETCH_MAPPINGS_FAILED'; export const ADD_NEW_MAPPING_PENDING = 'ADD_NEW_MAPPING_PENDING'; export const ADD_NEW_MAPPING_SUCCESS = 'ADD_NEW_MAPPING_SUCCESS'; export const ADD_NEW_MAPPING_FAILED = 'ADD_NEW_MAPPING_FAILED'; + +export const FETCH_INCIDENTS_PENDING = 'FETCH_INCIDENTS_PENDING'; +export const FETCH_INCIDENTS_SUCCESS = 'FETCH_INCIDENTS_SUCCESS'; +export const FETCH_INCIDENTS_FAILED = 'FETCH_INCIDENTS_FAILED'; diff --git a/client/src/store/reducers/incidentsReportReducer.js b/client/src/store/reducers/incidentsReportReducer.js new file mode 100644 index 0000000..85ca58d --- /dev/null +++ b/client/src/store/reducers/incidentsReportReducer.js @@ -0,0 +1,38 @@ +import { + FETCH_INCIDENTS_PENDING, + FETCH_INCIDENTS_SUCCESS, + FETCH_INCIDENTS_FAILED, +} from '../constants'; + +const initialState = { + pending: false, + result: null, + error: null, +}; + +export const incidentsReport = (state, action) => { + state = state || initialState; + action = action || {}; + + switch(action.type){ + case FETCH_INCIDENTS_PENDING: + return Object.assign({}, state, { + pending: true, + error: null, + }); + case FETCH_INCIDENTS_SUCCESS: + return Object.assign({}, state, { + pending: false, + result: action.payload, + error: null, + }); + case FETCH_INCIDENTS_FAILED: + return Object.assign({}, state, { + pending: false, + result: {}, + error: action.payload, + }); + default: + return state; + } +}; diff --git a/client/src/store/reducers/index.js b/client/src/store/reducers/index.js index ed2a547..3c060be 100644 --- a/client/src/store/reducers/index.js +++ b/client/src/store/reducers/index.js @@ -1,12 +1,14 @@ -import { combineReducers } from "redux"; +import { combineReducers } from 'redux'; -import { doorLockData} from "./doorLockReducers"; -import { mappingsData } from "./mappingsReducer"; +import { doorLockData} from './doorLockReducers'; +import { mappingsData } from './mappingsReducer'; import { addMapping } from './addMappingReducer'; +import { incidentsReport } from './incidentsReportReducer'; export const rootReducer = combineReducers({ doorLockData, mappingsData, addMapping, + incidentsReport, }); From d134f7ac4b881d425810e3d38c8e6a9df911a053 Mon Sep 17 00:00:00 2001 From: Bilal Catic Date: Mon, 17 Jun 2019 13:09:30 +0200 Subject: [PATCH 7/8] align fee column to the right --- client/src/scenes/IncidentsReport/index.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/client/src/scenes/IncidentsReport/index.js b/client/src/scenes/IncidentsReport/index.js index defc361..09a76cb 100644 --- a/client/src/scenes/IncidentsReport/index.js +++ b/client/src/scenes/IncidentsReport/index.js @@ -27,6 +27,12 @@ class IncidentsReport extends Component { const columnTitle = incidentsReportHeaderTitles[header]; if (columnTitle){ + const columnAlignments = { + left: 'left', + right: 'right', + }; + let columnContentsAlignment = columnAlignments.left; + columns.push({ Header: incidentsReportHeaderTitles[header], accessor: header, @@ -55,14 +61,16 @@ class IncidentsReport extends Component { case 'totalChargeFee': const totalFee = props.value ? props.value : props.row['_original'].incidentPrice; - cellValue = `$ ${totalFee}`; + const totalFeeFormatted = parseFloat(totalFee).toFixed(2); + cellValue = `$ ${totalFeeFormatted}`; + columnContentsAlignment = columnAlignments.right; break; default: cellValue = props.value; } - return {cellValue} + return
{cellValue}
} }); } From 5ffc6620f836efd10329420a470996b94c145d40 Mon Sep 17 00:00:00 2001 From: Bilal Catic Date: Mon, 17 Jun 2019 13:16:18 +0200 Subject: [PATCH 8/8] add default values for missing env variables --- services/integration/doorLockCharges.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/services/integration/doorLockCharges.js b/services/integration/doorLockCharges.js index e5a0545..9ce9228 100644 --- a/services/integration/doorLockCharges.js +++ b/services/integration/doorLockCharges.js @@ -97,8 +97,8 @@ const createUnlockedIncident = (reservation) => { const createUnscheduledUseIncident = (reservation, doorLockEntry) => { return new Promise((resolve, reject) => { - const timeResolution = parseInt(process.env.UNSCHEDULED_USE_TIME_RESOLUTION); - const chargePrice = parseFloat(process.env.UNSCHEDULED_USE_CHARGE_FEE); + const timeResolution = parseInt(process.env.UNSCHEDULED_USE_TIME_RESOLUTION) || 5; + const chargePrice = parseFloat(process.env.UNSCHEDULED_USE_CHARGE_FEE) || 5; const reservationEndTime = moment(reservation.end); const lockedTime = moment(doorLockEntry.timestamp); @@ -352,8 +352,8 @@ const getIncidentData = (reservation) => { getRelatedDoorLockEntries(reservation.start, doorLockEntriesEndTime, reservation.memberId, reservation.resourceId) .then((lockEntry) => { if (lockEntry){ - const timeResolution = parseInt(process.env.UNSCHEDULED_USE_TIME_RESOLUTION); - const chargePrice = parseFloat(process.env.UNSCHEDULED_USE_CHARGE_FEE); + const timeResolution = parseInt(process.env.UNSCHEDULED_USE_TIME_RESOLUTION) || 5 + const chargePrice = parseFloat(process.env.UNSCHEDULED_USE_CHARGE_FEE) || 5; const reservationEndTime = moment(reservation.end); const lockedTime = moment(lockEntry.timestamp);