diff --git a/client/src/components/DateRangePicker/index.js b/client/src/components/DateRangePicker/index.js
index 74d23f7..cb2d2c4 100644
--- a/client/src/components/DateRangePicker/index.js
+++ b/client/src/components/DateRangePicker/index.js
@@ -73,17 +73,25 @@ class DateRangePicker extends Component {
render() {
const { startDate, endDate, error, startDateLabel, endDateLabel } = this.state;
+ const { inlineButton } = this.props;
const buttonLabel = this.props.buttonLabel || 'Save';
const startDateValue = startDate.format(defaultDateFormat);
const endDateValue = endDate.format(defaultDateFormat);
+ const buttonRender = (
+
+ { inlineButton && }
+ {buttonLabel}
+
+ );
+
return (
);
diff --git a/client/src/components/MainMenu/index.js b/client/src/components/MainMenu/index.js
index 6890a4c..1fd8f65 100644
--- a/client/src/components/MainMenu/index.js
+++ b/client/src/components/MainMenu/index.js
@@ -7,14 +7,19 @@ import { mainMenuItems } from '../../constants/menuItems';
const MainMenu = () =>
();
diff --git a/client/src/components/MemberIncidentsTable/index.js b/client/src/components/MemberIncidentsTable/index.js
index 2e6b44f..6f79f59 100644
--- a/client/src/components/MemberIncidentsTable/index.js
+++ b/client/src/components/MemberIncidentsTable/index.js
@@ -2,6 +2,7 @@ import React from 'react';
import { Loader } from 'semantic-ui-react';
import ReactTable from 'react-table';
import 'react-table/react-table.css';
+import { NavLink } from 'react-router-dom';
import {incidentsReportHeaderTitles} from '../../constants/menuItems';
import {
@@ -13,7 +14,7 @@ import {
const MemberIncidentsTable = props => {
- const { loading, title } = props;
+ const { loading, title, openMemberSummaryOnMemberClick } = props;
const incidents = props.incidents ? props.incidents : [];
const columns = [];
@@ -34,9 +35,15 @@ const MemberIncidentsTable = props => {
Header: incidentsReportHeaderTitles[header],
accessor: header,
Cell: props => {
- let cellValue;
+ let cellValue = '';
+ let urlValue = undefined;
switch (props.column.id) {
+ case 'memberName':
+ const memberId = props.row['_original'].memberId;
+ urlValue = `/practice-summary-report/${memberId}`;
+ cellValue = props.value;
+ break;
case 'incidentType':
cellValue = incidentDescriptions[props.value];
break;
@@ -68,7 +75,17 @@ const MemberIncidentsTable = props => {
cellValue = props.value;
}
- return {cellValue}
+ if (openMemberSummaryOnMemberClick && urlValue){
+ return {cellValue}
+ }else{
+ return {cellValue}
+ }
+
+ // return
+ // {cellValue}
+ //
+
+ // return
}
});
}
diff --git a/client/src/constants/menuItems.js b/client/src/constants/menuItems.js
index e2ab184..07b6c96 100644
--- a/client/src/constants/menuItems.js
+++ b/client/src/constants/menuItems.js
@@ -6,24 +6,34 @@ import PracticeSummaryReport from '../scenes/PracticeSummaryReport';
export const mainMenuItems = [
{
id: 'home',
+ showInMenu: true,
title: 'Home',
url: '/',
component: Home,
},
{
id: 'practiceSummaryReport',
+ showInMenu: true,
title: 'Practice Summary Report',
url: '/practice-summary-report',
component: PracticeSummaryReport,
},
+ {
+ id: 'practiceSummaryReportWithMemberId',
+ showInMenu: false,
+ url: '/practice-summary-report/:memberId',
+ component: PracticeSummaryReport,
+ },
{
id: 'report',
+ showInMenu: true,
title: 'Incidents Report',
url: '/incidents-report',
component: IncidentsReport,
},
{
id: 'uploadDLockData',
+ showInMenu: true,
title: 'DLock',
url: '/dlock',
component: UploadDLockData,
diff --git a/client/src/scenes/IncidentsReport/index.js b/client/src/scenes/IncidentsReport/index.js
index 6501d47..fa464ac 100644
--- a/client/src/scenes/IncidentsReport/index.js
+++ b/client/src/scenes/IncidentsReport/index.js
@@ -24,7 +24,7 @@ class IncidentsReport extends Component {
-
+
);
}
diff --git a/client/src/scenes/PracticeSummaryReport/components/MemberSummary.js b/client/src/scenes/PracticeSummaryReport/components/MemberSummary.js
index 687fed0..6f15622 100644
--- a/client/src/scenes/PracticeSummaryReport/components/MemberSummary.js
+++ b/client/src/scenes/PracticeSummaryReport/components/MemberSummary.js
@@ -11,7 +11,6 @@ const MemberSummary = props => {
let totalUnlockedFees = 0;
incidents.forEach((incident) => {
- console.log(incident);
switch (incident.incidentType) {
case UNSCHEDULED_INCIDENT:
totalUnscheduledFees += parseFloat(incident.totalChargeFee);
@@ -19,6 +18,8 @@ const MemberSummary = props => {
case UNLOCKED_INCIDENT:
totalUnlockedFees += parseFloat(incident.incidentPrice);
break;
+ default:
+ break;
}
});
@@ -65,5 +66,4 @@ const MemberSummary = props => {
);
};
-
export default MemberSummary;
diff --git a/client/src/scenes/PracticeSummaryReport/index.js b/client/src/scenes/PracticeSummaryReport/index.js
index 70fa155..32ea48e 100644
--- a/client/src/scenes/PracticeSummaryReport/index.js
+++ b/client/src/scenes/PracticeSummaryReport/index.js
@@ -16,7 +16,7 @@ class PracticeSummaryReport extends Component {
this.state = {
dateRange: null,
- memberId: null,
+ memberId: props.match.params.memberId,
};
}
@@ -52,7 +52,7 @@ class PracticeSummaryReport extends Component {
-
+
diff --git a/controllers/doorLock.js b/controllers/doorLock.js
index 6e9626f..c34248a 100644
--- a/controllers/doorLock.js
+++ b/controllers/doorLock.js
@@ -27,7 +27,13 @@ const uploadDoorLockData = (req, res) => {
parserResults.forEach((parserResult) => {
parsedData.push(...parserResult.parsedData);
parserErrors.push(...parserResult.errors);
- unknownMembers.push(...parserResult.unknownMembers);
+
+ parserResult.unknownMembers.forEach((newUnknownMember) => {
+ // Check if member is already labeled as unknown in different file
+ if (!unknownMembers.find((unknownMember) => unknownMember.details === newUnknownMember.details)){
+ unknownMembers.push(newUnknownMember);
+ }
+ });
});
const asyncWriteJobs = [];
diff --git a/services/doorLock/doorLock.js b/services/doorLock/doorLock.js
index c59c6e7..803401e 100644
--- a/services/doorLock/doorLock.js
+++ b/services/doorLock/doorLock.js
@@ -3,10 +3,11 @@
const db = require('../../models');
const fs = require('fs');
const csv = require('csv-parser');
-const moment = require('moment/moment');
+const moment = require('moment-timezone');
const Op = require('sequelize').Op;
const {
+ UI_TIMEZONE,
USER_ENTRY_EVENT,
ENABLE_PASSAGE_MODE,
DISABLE_PASSAGE_MODE,
@@ -38,7 +39,7 @@ const parseDoorLockDataFile = (file) => {
return new Promise ((resolve, reject) => {
const results = [];
const errors = [];
- const unknownMembers = [];
+ const unknownMembersToReport = [];
let isValidFile = true;
const prefetchDataJobs = [getMappingsFromDatabase(), fetchAllMembers()];
@@ -48,6 +49,11 @@ const parseDoorLockDataFile = (file) => {
const mappings = result[0];
const allMembers = result[1];
+ const membersMap = {};
+ const unknownMembersMap = {};
+
+ allMembers.forEach((member) => membersMap[member.name] = member);
+
const mappingFromFileName = extractMappingFromFileName(file.name);
const mappingObject = checkIfMappingExsists(mappingFromFileName, mappings);
if (!mappingObject){
@@ -104,13 +110,15 @@ const parseDoorLockDataFile = (file) => {
const secondEntry = results[i+1];
if (firstEntry && (firstEntry.event === USER_ENTRY_EVENT)){
- const memberObject = allMembers.find(member => member.name === firstEntry.name);
+ const memberObject = membersMap[firstEntry.name];
if (!memberObject){
//Check if member is already labeled as unknown
- const unknownMember = unknownMembers.find((member) => member.details === firstEntry.name);
+ const unknownMember = unknownMembersMap[firstEntry.name];
+
if (!unknownMember){
- unknownMembers.push({
+ unknownMembersMap[firstEntry.name] = firstEntry.name;
+ unknownMembersToReport.push({
error: csvParserErrors.UNKNOWN_MEMBER,
details: firstEntry.name,
file: file.name,
@@ -123,7 +131,7 @@ const parseDoorLockDataFile = (file) => {
doorLockEvents.USER_UNLOCKED : doorLockEvents.USER_LOCKED;
const dateTimeString = `${firstEntry.date} ${firstEntry.time}`;
- const timestamp = moment.utc(dateTimeString, 'MM/DD/YY HH:mm:ss A').toISOString();
+ const timestamp = moment.tz(dateTimeString, 'MM/DD/YY HH:mm:ss A', UI_TIMEZONE).tz('UTC').toISOString();
//Verify that member is registered in OfficeRnD system
if (memberObject){
@@ -159,7 +167,7 @@ const parseDoorLockDataFile = (file) => {
}
resolve({
parsedData,
- unknownMembers,
+ unknownMembers: unknownMembersToReport,
errors
});
});
diff --git a/services/integration/doorLockCharges.js b/services/integration/doorLockCharges.js
index 9ce9228..2a8903e 100644
--- a/services/integration/doorLockCharges.js
+++ b/services/integration/doorLockCharges.js
@@ -21,160 +21,6 @@ const getSortedIncidentsForMember = (memberId) => {
})
};
-const createUnlockedIncident = (reservation) => {
- return new Promise((resolve, reject) => {
- const { reservationId, memberId, resourceId, start, end } = reservation;
-
- getLastIncidentForMember(memberId)
- .then(incidents => {
- const lastIncident = incidents && incidents[0] ? incidents[0] : undefined;
-
- const incident = {
- reservationId,
- memberId,
- resourceId,
- bookingStart: start,
- bookingEnd: end,
- incidentLevel: null,
- incidentLevelPrice: null,
- };
-
- console.log('=> UNLOCKED INCIDENT');
- console.log('\tMember : ', memberId);
- console.log('\tStart : ', start);
- console.log('\tEnd : ', end);
- console.log('\tMore details : ');
-
- /*
- if (lastIncident){
- const lastIncidentLevel = lastIncident.incidentLevel;
- const lastIncidentBeginningOfTheMonth = moment(lastIncident.bookingStart).startOf('month');
- const beginningOfTheMonth = moment.utc().startOf('month');
-
- const timePassedFromLastIncident = Math.abs(beginningOfTheMonth.diff(lastIncidentBeginningOfTheMonth, 'months'));
-
- if (timePassedFromLastIncident >= 6){
- console.log('\t\t-> This is first incident for this member in last 6 months');
- incident.incidentLevel = unlockedIncidentLevelsPrices.UNLOCKED_0.title;
- incident.incidentLevelPrice = unlockedIncidentLevelsPrices.UNLOCKED_0.price;
- } else {
- console.log('\t\t-> This member had incident(s) in past 6 months !!!');
- incident.incidentLevel = lastIncidentLevel;
- incident.incidentLevelPrice = unlockedIncidentLevelsPrices[lastIncidentLevel].price;
- }
- console.log('\t\tLast incident details : ');
- console.log('\t\tStart : ', lastIncident.bookingStart);
- console.log('\t\tCalculated diff : ', timePassedFromLastIncident);
- console.log('\t\t------------------');
- console.log('\tNew incident level : ', incident.incidentLevel);
- } else {
- console.log('\t\tThis is first incident for this member, EVER !');
- incident.incidentLevel = unlockedIncidentLevelsPrices.UNLOCKED_0.title;
- incident.incidentLevelPrice = unlockedIncidentLevelsPrices.UNLOCKED_0.price;
- }
- */
-
- db.unlockedIncident.findOrCreate({
- where: {
- reservationId,
- memberId,
- resourceId,
- bookingStart: start,
- bookingEnd: end,
- },
- defaults: {
- ...incident
- }
- })
- .then(()=>resolve())
- .catch((error)=>reject(error));
- })
- .catch((error) => {
- reject(error);
- });
- });
-};
-
-const createUnscheduledUseIncident = (reservation, doorLockEntry) => {
- return new Promise((resolve, reject) => {
- 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);
- const timeDifference = Math.abs(reservationEndTime.diff(lockedTime, 'minutes'));
-
- const timeIntervalsToCharge = Math.floor(timeDifference / timeResolution);
- const totalChargeFee = timeIntervalsToCharge * chargePrice;
-
- if (timeIntervalsToCharge > 0){
- const incident = {
- reservationId: reservation.reservationId,
- memberId: reservation.memberId,
- resourceId: reservation.resourceId,
- bookingStart: reservation.start,
- bookingEnd: reservation.end,
- doorLockEventTimestamp: doorLockEntry.timestamp,
- doorLockEventType: doorLockEntry.event,
- chargePrice,
- timeIntervalsToCharge,
- totalChargeFee,
- };
-
- db.unscheduledIncident.findOrCreate({where: {...incident}, defaults: {...incident}})
- .then(()=>resolve())
- .catch((error)=>reject(error));
- }else{
- resolve();
- }
- });
-};
-
-const createDoorLockIncident = (reservation, doorLockEntry) => {
- return new Promise((resolve, reject) => {
- if (!doorLockEntry){
- // Check if there is unlock entry for this reservation
- getUnlockEntryForReservation(reservation)
- .then((unlockEntry) => {
- if (!unlockEntry){
- // check if there is back-to-back booking before current one
- getFirstPreviousBooking(reservation)
- .then((previousReservation) => {
- if (previousReservation){
- const previousReservationEnd = moment(previousReservation.end);
- const currentReservationStart = moment(reservation.start);
- const timeDifference = Math.abs(currentReservationStart.diff(previousReservationEnd, 'minutes'));
-
- const maxBackToBackDifference = parseInt(process.env.MAX_BACK_TO_BACK_DIFFERENCE) || 0;
- if (timeDifference <= maxBackToBackDifference) {
- createUnlockedIncident(reservation)
- .then(() => resolve())
- .catch((error) => reject(error));
- }else{
- resolve();
- }
- }else{
- resolve();
- }
- })
- .catch((error)=>reject(error));
- }else {
- createUnlockedIncident(reservation)
- .then(()=>resolve())
- .catch((error)=>reject(error));
- }
- })
- .catch((error) => {
- reject(error);
- });
- }else{
- createUnscheduledUseIncident(reservation, doorLockEntry)
- .then(()=>resolve())
- .catch((error) => reject(error));
- }
- });
-};
-
const insertUnscheduledIncidents = (incidents) => {
const asyncJobs = [];
incidents.forEach((incident) => {
@@ -333,8 +179,8 @@ const getIncidentData = (reservation) => {
if (nextReservation){
// Check if next reservations is immediately after (back to back reservation)
// If yes, then there is no need to check door lock entries related to this booking
- const firstReservationEnd = moment(reservation.end);
- const secondReservationStart = moment(nextReservation.start);
+ const firstReservationEnd = moment.utc(reservation.end);
+ const secondReservationStart = moment.utc(nextReservation.start);
const timeDifference = Math.abs(secondReservationStart.diff(firstReservationEnd, 'minutes'));
const maxBackToBackDifference = parseInt(process.env.MAX_BACK_TO_BACK_DIFFERENCE) || 0;
@@ -355,8 +201,8 @@ const getIncidentData = (reservation) => {
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);
+ const reservationEndTime = moment.utc(reservation.end);
+ const lockedTime = moment.utc(lockEntry.timestamp);
const timeDifference = Math.abs(reservationEndTime.diff(lockedTime, 'minutes'));
const timeIntervalsToCharge = Math.floor(timeDifference / timeResolution);
@@ -391,8 +237,8 @@ const getIncidentData = (reservation) => {
getFirstPreviousBooking(reservation)
.then((previousReservation) => {
if (previousReservation){
- const previousReservationEnd = moment(previousReservation.end);
- const currentReservationStart = moment(reservation.start);
+ const previousReservationEnd = moment.utc(previousReservation.end);
+ const currentReservationStart = moment.utc(reservation.start);
const timeDifference = Math.abs(currentReservationStart.diff(previousReservationEnd, 'minutes'));
const maxBackToBackDifference = parseInt(process.env.MAX_BACK_TO_BACK_DIFFERENCE) || 0;
diff --git a/services/officeRnD/bookings.js b/services/officeRnD/bookings.js
index e63d3d3..db98a88 100644
--- a/services/officeRnD/bookings.js
+++ b/services/officeRnD/bookings.js
@@ -41,7 +41,7 @@ const getAllFinishedBookings = () => {
const filters = {
canceled: false,
end: {
- [Op.lt]: moment().toISOString()
+ [Op.lt]: moment.utc().toISOString()
}
};
@@ -56,8 +56,8 @@ const getAllFinishedBookings = () => {
const getFirstNextBooking = (reservation) => {
return new Promise ((resolve, reject) => {
- const {resourceId, start, timezone} = reservation;
- const endOfTheDay = moment.tz(start, timezone).endOf('Day').toISOString();
+ const { resourceId, start } = reservation;
+ const endOfTheDay = moment.utc(start).endOf('Day').toISOString();
const attributes = ['reservationId', 'memberId', 'resourceId', 'start', 'end', 'timezone'];
const filters = {
@@ -90,8 +90,8 @@ const getFirstNextBooking = (reservation) => {
const getFirstPreviousBooking = (reservation) => {
return new Promise ((resolve, reject) => {
- const {resourceId, start, timezone} = reservation;
- const startOfTheDay = moment.tz(start, timezone).startOf('Day').toISOString();
+ const { resourceId, start } = reservation;
+ const startOfTheDay = moment.utc(start).startOf('Day').toISOString();
const attributes = ['reservationId', 'memberId', 'resourceId', 'start', 'end', 'timezone'];
const filters = {