diff --git a/client/src/components/PromptMessage/index.js b/client/src/components/PromptMessage/index.js
new file mode 100644
index 0000000..e85e9ad
--- /dev/null
+++ b/client/src/components/PromptMessage/index.js
@@ -0,0 +1,35 @@
+import React, { Component } from 'react';
+import {Button, Modal} from "semantic-ui-react";
+
+
+class PromptMessage extends Component {
+ render() {
+ const {
+ show = false,
+ size = 'tiny',
+ title = '',
+ message = '',
+ noActionLabel = 'No',
+ yesActionLabel = 'Yes',
+ yesActionIconName = 'checkmark',
+ onClose = null,
+ onActionNo = null,
+ onActionYes = null,
+ } = this.props;
+
+ return (
+
+ {title}
+
+ {message}
+
+
+
+
+
+
+ );
+ }
+}
+
+export default PromptMessage;
diff --git a/client/src/constants/menuItems.js b/client/src/constants/menuItems.js
index 2c7b8bf..2a9102c 100644
--- a/client/src/constants/menuItems.js
+++ b/client/src/constants/menuItems.js
@@ -3,6 +3,7 @@ import Home from '../scenes/Home';
import IncidentsReport from '../scenes/IncidentsReport';
import SpecificMemberIncidentsReport from '../scenes/SpecificMemberIncidentsReport';
import MemberPracticeSummaryReport from '../scenes/MemberPracticeSummaryReport';
+import RoomOfficeNameMapping from '../scenes/RoomOfficeNameMapping';
export const mainMenuItems = [
{
@@ -46,4 +47,11 @@ export const mainMenuItems = [
url: '/dlock',
component: UploadDLockData,
},
+ {
+ id: 'roomOfficeNameMapping',
+ showInMenu: true,
+ title: 'Room Office Name Mapping',
+ url: '/room-office-name-mapping',
+ component: RoomOfficeNameMapping,
+ }
];
diff --git a/client/src/scenes/RoomOfficeNameMapping/components/SingleMapping.js b/client/src/scenes/RoomOfficeNameMapping/components/SingleMapping.js
new file mode 100644
index 0000000..2e7d36d
--- /dev/null
+++ b/client/src/scenes/RoomOfficeNameMapping/components/SingleMapping.js
@@ -0,0 +1,152 @@
+import React, { Component } from 'react';
+import { connect } from 'react-redux';
+
+import { Table, Label, Icon, Dropdown } from 'semantic-ui-react';
+import PromptMessage from '../../../components/PromptMessage';
+import { deleteMapping, updateMapping } from '../../../store/actions';
+
+class SingleMapping extends Component {
+ state = {showDeletePrompt: false, newOfficeId: null, newResourceId: null};
+
+ deleteMapping = () => {
+ this.setState({showDeletePrompt: true});
+ };
+
+ onPromptClose = () => {
+ this.setState({showDeletePrompt: false});
+ };
+
+ onPromptYes = () => {
+ const { singleMapping: { id }, deleteMapping } = this.props;
+ if (parseInt(id)){
+ deleteMapping(id);
+ }
+ this.onPromptClose();
+ };
+
+ onOfficeChange = (event, data) => {
+ const newOfficeId = data.value;
+ const { allResourceOptions, officeId: oldOfficeId } = this.props;
+
+ if (oldOfficeId !== newOfficeId){
+ let newResourceId = null;
+ const roomOptionsForOffice = allResourceOptions[newOfficeId] && allResourceOptions[newOfficeId].roomOptions ?
+ allResourceOptions[newOfficeId].roomOptions : [];
+
+ if (roomOptionsForOffice.length > 0){
+ newResourceId = roomOptionsForOffice[0].value;
+ }
+
+ this.setState({newOfficeId, newResourceId});
+ }else{
+ this.setState({newOfficeId: null, newResourceId: null});
+ }
+ };
+
+ onResourceChange = (event, data) => {
+ const newResourceId = data.value;
+ const oldResourceId = this.props.resourceId;
+
+ if (oldResourceId !== newResourceId){
+ this.setState({newResourceId});
+ }else{
+ this.setState({newResourceId: null});
+ }
+ };
+
+ onMappingUpdate = () => {
+ const { singleMapping: { id, officeSlug, resourceSlug }, updateMapping, officeId, resourceId } = this.props;
+ const { newOfficeId, newResourceId } = this.state;
+
+ const selectedOfficeId = newOfficeId ? newOfficeId : officeId;
+ const selectedResourceId = newResourceId ? newResourceId : resourceId;
+
+ if (parseInt(id)){
+
+ const modifiedMapping = {
+ officeSlug,
+ resourceSlug,
+ officeId: selectedOfficeId,
+ resourceId: selectedResourceId,
+ };
+
+ updateMapping(id, modifiedMapping);
+ this.setState({newOfficeId: null, newResourceId: null});
+ }
+ };
+
+ render() {
+ const { singleMapping: { officeSlug, resourceSlug } } = this.props;
+ const { officeId, resourceId, officeOptions, allResourceOptions, pendingMappings } = this.props;
+ const { showDeletePrompt, newOfficeId, newResourceId } = this.state;
+
+ const selectedOfficeId = newOfficeId ? newOfficeId : officeId;
+ const selectedResourceId = newResourceId ? newResourceId : resourceId;
+
+ const resourceOptions = allResourceOptions && officeId ? allResourceOptions[selectedOfficeId].roomOptions : [];
+
+ const enableActions = !pendingMappings;
+ const enableSave = enableActions && ((newOfficeId !== null) || (newResourceId !== null));
+ const saveIconColor = enableSave ? 'green' : null;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+const mapStateToProps = (state) => ({
+ pendingMappings: state.mappingsData.pending,
+});
+
+const mapDispatchToProps = (dispatch) => ({
+ deleteMapping: (id) => deleteMapping(dispatch, id),
+ updateMapping: (id, mapping) => updateMapping(dispatch, id, mapping),
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(SingleMapping);
diff --git a/client/src/scenes/RoomOfficeNameMapping/index.js b/client/src/scenes/RoomOfficeNameMapping/index.js
new file mode 100644
index 0000000..efaebf2
--- /dev/null
+++ b/client/src/scenes/RoomOfficeNameMapping/index.js
@@ -0,0 +1,99 @@
+import React, { Component } from 'react';
+import { connect } from 'react-redux';
+import { Container, Message, Loader, Table } from 'semantic-ui-react';
+
+import MainMenu from '../../components/MainMenu';
+import { fetchMappings } from '../../store/actions';
+import SingleMapping from './components/SingleMapping';
+
+class RoomOfficeNameMapping extends Component {
+
+ componentDidMount() {
+ const { fetchMappings } = this.props;
+
+ fetchMappings();
+ }
+
+ render () {
+ const { pendingMappings, mappings } = this.props;
+ const existingMappings = mappings && mappings.existingMappings ? mappings.existingMappings : [];
+ const offices = mappings && mappings.offices ? mappings.offices : [];
+ const resources = mappings && mappings.resources ? mappings.resources : [];
+
+ const officeDropdownOptions = offices.map((office) => {
+ const { officeId, officeName } = office;
+ return {
+ key: officeId,
+ value: officeId,
+ text: officeName,
+ }
+ });
+
+ const allResourceOptions = {};
+ resources.forEach((resource) => {
+ const { resourceId, resourceName, officeId } = resource;
+
+ if (!allResourceOptions[officeId]){
+ allResourceOptions[officeId] = {
+ roomOptions: []
+ }
+ }
+
+ allResourceOptions[officeId].roomOptions.push({
+ key: resourceId,
+ value: resourceId,
+ text: resourceName,
+ });
+ });
+
+ return (
+
+
+
+ Room-Office-Name Mappings
+
+
+
+
+
+ File name
+ Office
+ Resource
+ Actions
+
+
+
+ {
+ existingMappings.map((singleMapping) => {
+ const { officeId, resourceId } = singleMapping;
+ return
+ })
+ }
+
+
+
+
+ To add the mapping, upload new DL lock files and you will be prompted with new mapping options if the file name contains the unknown location
+
+
+ );
+ }
+}
+
+const mapStateToProps = (state) => ({
+ pendingMappings: state.mappingsData.pending,
+ mappings: state.mappingsData.result,
+});
+
+const mapDispatchToProps = (dispatch) => ({
+ fetchMappings: () => fetchMappings(dispatch),
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(RoomOfficeNameMapping);
diff --git a/client/src/store/actions/integrationActions.js b/client/src/store/actions/integrationActions.js
index d2b4d1f..bef2eb7 100644
--- a/client/src/store/actions/integrationActions.js
+++ b/client/src/store/actions/integrationActions.js
@@ -38,6 +38,30 @@ export const fetchMappings = (dispatch) => {
});
};
+export const deleteMapping = (dispatch, mappingId) => {
+ dispatch({type: FETCH_MAPPINGS_PENDING});
+ API.delete(`integration/mappings/${mappingId}`)
+ .then(response => {
+ dispatch({type: FETCH_MAPPINGS_SUCCESS, payload: response.data});
+ })
+ .catch(error => {
+ dispatch({type: FETCH_MAPPINGS_FAILED, payload: error.response});
+ });
+};
+
+export const updateMapping = (dispatch, id, mapping) => {
+ dispatch({type: FETCH_MAPPINGS_PENDING});
+ API.put(`integration/mappings/${id}`, {
+ mapping
+ })
+ .then(response => {
+ dispatch({type: FETCH_MAPPINGS_SUCCESS, payload: response.data});
+ })
+ .catch(error => {
+ dispatch({type: FETCH_MAPPINGS_FAILED, payload: error.response});
+ });
+};
+
export const addNewMapping = (dispatch, mapping) => {
dispatch({type: ADD_NEW_MAPPING_PENDING});
API.post('integration/mappings', {
diff --git a/controllers/integration.js b/controllers/integration.js
index 6b4c282..5b17d23 100644
--- a/controllers/integration.js
+++ b/controllers/integration.js
@@ -2,7 +2,7 @@
const moment = require('moment-timezone');
-const { getMappingsFromDatabase, fetchOffices, fetchResources, saveNewMappingToDatabase } = require('../services/officeRnD/resources');
+const { getMappingsFromDatabase, fetchOffices, fetchResources, saveNewMappingToDatabase, deleteMappingById, updateMappingById } = require('../services/officeRnD/resources');
const { getAllIncidents, getMemberPracticeSummaryReport } = require('../services/integration/reports');
const { getMembersFeesForDateRange } = require('../services/integration/invoiceIntegration');
const { deleteFeesFromORD, addFeesToORD } = require('../services/officeRnD/fees');
@@ -13,7 +13,7 @@ const { checkIfProcessing } = require('../services/integration/processingStatus'
const { UI_TIMEZONE } = require('../constants/constants');
const getKnownOfficeResourceMappings = (req, res) => {
- const dataToFetch = [getMappingsFromDatabase(), fetchOffices(), fetchResources() ];
+ const dataToFetch = [getMappingsFromDatabase(), fetchOffices(), fetchResources()];
Promise.all(dataToFetch)
.then(result => {
@@ -24,6 +24,7 @@ const getKnownOfficeResourceMappings = (req, res) => {
});
})
.catch(error => {
+ console.log('Error with fetching mappings : ', error);
res.status(500).send();
});
};
@@ -41,6 +42,43 @@ const addNewMapping = (req, res) => {
}
};
+const deleteMapping = (req, res) => {
+ const mappingId = req.params.mappingId;
+
+ if (mappingId && parseInt(mappingId)){
+ deleteMappingById(mappingId)
+ .then(() => {
+ getKnownOfficeResourceMappings(req, res);
+ })
+ .catch((error) => {
+ console.log('Error deleting mapping with id = ', mappingId);
+ console.log(error);
+ res.status(500).send();
+ });
+ }else{
+ getKnownOfficeResourceMappings(req, res);
+ }
+};
+
+const updateMapping = (req, res) => {
+ const mappingId = req.params.mappingId;
+ const mapping = req.body && req.body.mapping ? req.body.mapping : null;
+
+ if (mappingId && parseInt(mappingId)){
+ updateMappingById(mappingId, mapping)
+ .then(() => {
+ getKnownOfficeResourceMappings(req, res);
+ })
+ .catch((error) => {
+ console.log('Error updating mapping with id = ', mappingId);
+ console.log(error);
+ res.status(500).send();
+ });
+ }else{
+ getKnownOfficeResourceMappings(req, res);
+ }
+};
+
const getAllIncidentsController = (req, res) => {
const dateRange = {
startDate: req.params.startDate,
@@ -170,6 +208,8 @@ const getPracticeSummaryReport = (req, res) => {
module.exports = {
getKnownOfficeResourceMappings,
addNewMapping,
+ deleteMapping,
+ updateMapping,
getAllIncidentsController,
getMemberIncidents,
addFees,
diff --git a/routes/index.js b/routes/index.js
index 27add39..c8d30fe 100644
--- a/routes/index.js
+++ b/routes/index.js
@@ -6,6 +6,8 @@ const { fetchMembersList } = require('../controllers/officeRnD');
const {
getKnownOfficeResourceMappings,
addNewMapping,
+ deleteMapping,
+ updateMapping,
getAllIncidentsController,
getMemberIncidents,
addFees,
@@ -23,6 +25,8 @@ router.get('/', apiStatusCheck);
router.post('/doorLock/upload', uploadDoorLockData);
router.get('/integration/mappings', getKnownOfficeResourceMappings);
router.post('/integration/mappings', addNewMapping);
+router.delete('/integration/mappings/:mappingId', deleteMapping);
+router.put('/integration/mappings/:mappingId', updateMapping);
router.get('/integration/report/member/:memberId/:startDate/:endDate', getMemberIncidents);
router.get('/integration/report/allIncidents/:startDate/:endDate', getAllIncidentsController);
diff --git a/services/officeRnD/resources.js b/services/officeRnD/resources.js
index 37d73b7..918bc68 100644
--- a/services/officeRnD/resources.js
+++ b/services/officeRnD/resources.js
@@ -70,7 +70,19 @@ const getResourceMappings = () => {
};
const getMappingsFromDatabase = () => {
- return db.officeResourceMapping.findAll();
+ return db.officeResourceMapping.findAll({order: [['id']]});
+};
+
+const deleteMappingById = (id) => {
+ return db.officeResourceMapping.destroy({where: {id}});
+};
+
+const updateMappingById = (id, mapping) => {
+ if (mapping.id) {
+ delete mapping.id;
+ }
+
+ return db.officeResourceMapping.update(mapping, {where: {id}});
};
const saveNewMappingToDatabase = (mapping) => {
@@ -83,4 +95,6 @@ module.exports = {
fetchResources,
getResourceMappings,
saveNewMappingToDatabase,
+ deleteMappingById,
+ updateMappingById,
};