From d02dc488a9b03be6a1467ff8fa4d77ae5b773eee Mon Sep 17 00:00:00 2001 From: Jerry Ren Date: Wed, 27 Sep 2023 16:32:17 -0400 Subject: [PATCH 01/36] init commit --- src/controllers/userProfileController.js | 57 ++++++++++++++++++++++++ src/routes/userProfileRouter.js | 3 ++ 2 files changed, 60 insertions(+) diff --git a/src/controllers/userProfileController.js b/src/controllers/userProfileController.js index 5f36e91a2..4ee2c7f95 100644 --- a/src/controllers/userProfileController.js +++ b/src/controllers/userProfileController.js @@ -2,6 +2,7 @@ const moment = require('moment-timezone'); const mongoose = require('mongoose'); const bcrypt = require('bcryptjs'); +const axios = require('axios') const moment_ = require('moment'); const jwt = require('jsonwebtoken'); @@ -121,6 +122,7 @@ const userProfileController = function (UserProfile) { }, }); + if (userByEmail) { res.status(400).send({ error: 'That email address is already in use. Please choose another email address.', @@ -129,6 +131,47 @@ const userProfileController = function (UserProfile) { return; } + // In dev environment, Check if email exists in beta email + if (process.env.dbName === 'hgnData_dev') { + console.log("🚀 ~ file: userProfileController.js:135 ~ postUserProfile ~ Inside dev environment:") + + const email = "devadmin@hgn.net" + const password = "DeveloperPassword100%!" + const url = "https://hgn-rest-dev.azurewebsites.net/api/" + try { + // log in with axios + let response = await axios.post(url + "login", { + email: email, + password: password + }) + console.log("🚀 ~ file: userProfileController.js:146 ~ postUserProfile ~ response.data:", response.data) + const token = response.data.token + + response = await axios.get(url + "userprofile", { + headers: { + Authorization: token + } + }) + const userProfiles = response.data + const emails = userProfiles.map(profile => profile.email) + console.log("🚀 ~ file: userProfileController.js:156 ~ postUserProfile ~ emails:", emails) + + + // Check if email entered is in this list of real emails + if (!(emails.includes(email))) { + console.log("🚀 ~ file: userProfileController.js:163 ~ postUserProfile ~ email is NOT in emails") + res.status(400).send({ + error: 'That email address does not match a real email address in the beta database. Please enter a real email address associated with an account in the beta database.', + type: 'email', + }); + return; + } + } catch (error) { + console.log("🚀 ~ file: userProfileController.js:147 ~ postUserProfile ~ error:", error) + } + + } + /** * * Turn on and off the duplicate phone number checker by changing * the value of duplicatePhoneNumberCheck variable. @@ -814,6 +857,19 @@ const userProfileController = function (UserProfile) { res.status(200).send({ refreshToken: currentRefreshToken }); }; + const getUserEmails = async (req, res) => { + try { + const userProfiles = await UserProfile.find({}, 'email').lean(); + const userEmails = userProfiles.map(profile => profile.email) + console.log("🚀 ~ file: userProfileController.js:821 ~ getUserEmails ~ userEmails:", userEmails) + res.status(200).send(userEmails); + } catch (error) { + console.error(error); + res.status(500).send('Internal Server Error'); + } + }; + + return { postUserProfile, getUserProfiles, @@ -831,6 +887,7 @@ const userProfileController = function (UserProfile) { getUserByName, getAllUsersWithFacebookLink, refreshToken, + getUserEmails, }; }; diff --git a/src/routes/userProfileRouter.js b/src/routes/userProfileRouter.js index 9032359a8..0850aa27d 100644 --- a/src/routes/userProfileRouter.js +++ b/src/routes/userProfileRouter.js @@ -10,6 +10,9 @@ const routes = function (userProfile) { .get(controller.getUserProfiles) .post(controller.postUserProfile); + userProfileRouter.route('/userProfile/emails') + .get(controller.getUserEmails) + userProfileRouter.route('/userProfile/:userId') .get(controller.getUserById) .put(controller.putUserProfile) From 4f52d0ad8c1b9bd5f23530faecbaf10ab832ee13 Mon Sep 17 00:00:00 2001 From: Jerry Ren Date: Thu, 19 Oct 2023 18:57:39 -0400 Subject: [PATCH 02/36] Call login route in beta server to validate email Whenever an Administrator user is created, utilized newly added 'req.body.betaEmail' and 'req.body.betaPassword' to validate that the provided email address exists in the beta database. --- src/controllers/userProfileController.js | 37 +++++++++--------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/src/controllers/userProfileController.js b/src/controllers/userProfileController.js index 8d5b9a752..58cb562c2 100644 --- a/src/controllers/userProfileController.js +++ b/src/controllers/userProfileController.js @@ -106,6 +106,10 @@ const userProfileController = function (UserProfile) { }; const postUserProfile = async function (req, res) { + + console.log("🚀 ~ file: userProfileController.js:110 ~ postUserProfile ~ req.body.betaEmail:", req.body.betaEmail) + console.log("🚀 ~ file: userProfileController.js:112 ~ postUserProfile ~ req.body.betaPassword:", req.body.betaPassword) + if (!await hasPermission(req.body.requestor, 'postUserProfile')) { res.status(403).send('You are not authorized to create new users'); return; @@ -137,9 +141,9 @@ const userProfileController = function (UserProfile) { if (process.env.dbName === 'hgnData_dev') { console.log("🚀 ~ file: userProfileController.js:135 ~ postUserProfile ~ Inside dev environment:") - const email = "devadmin@hgn.net" - const password = "DeveloperPassword100%!" - const url = "https://hgn-rest-dev.azurewebsites.net/api/" + const email = req.body.betaEmail + const password = req.body.betaPassword + const url = "https://hgn-rest-beta.azurewebsites.net/api/" try { // log in with axios let response = await axios.post(url + "login", { @@ -148,28 +152,15 @@ const userProfileController = function (UserProfile) { }) console.log("🚀 ~ file: userProfileController.js:146 ~ postUserProfile ~ response.data:", response.data) const token = response.data.token + console.log("🚀 ~ file: userProfileController.js:155 ~ postUserProfile ~ token:", token) - response = await axios.get(url + "userprofile", { - headers: { - Authorization: token - } - }) - const userProfiles = response.data - const emails = userProfiles.map(profile => profile.email) - console.log("🚀 ~ file: userProfileController.js:156 ~ postUserProfile ~ emails:", emails) - - - // Check if email entered is in this list of real emails - if (!(emails.includes(email))) { - console.log("🚀 ~ file: userProfileController.js:163 ~ postUserProfile ~ email is NOT in emails") - res.status(400).send({ - error: 'That email address does not match a real email address in the beta database. Please enter a real email address associated with an account in the beta database.', - type: 'email', - }); - return; - } } catch (error) { - console.log("🚀 ~ file: userProfileController.js:147 ~ postUserProfile ~ error:", error) + console.log("🚀 ~ file: userProfileController.js:163 ~ postUserProfile ~ error:", error) + res.status(400).send({ + error: 'That email address does not match a real email address in the beta database. Please enter a real email address associated with an account in the beta database.', + type: 'email', + }); + return; } } From 844206b849a4ebef543c3bb0fb314c2d0285e000 Mon Sep 17 00:00:00 2001 From: Jerry Ren Date: Fri, 20 Oct 2023 21:16:24 -0400 Subject: [PATCH 03/36] Use actual email and password on beta login route Use req.body.actualEmail and req.body.actualPassword on the beta login route. If error caught, return 400 error. --- src/controllers/userProfileController.js | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/src/controllers/userProfileController.js b/src/controllers/userProfileController.js index 58cb562c2..65f202932 100644 --- a/src/controllers/userProfileController.js +++ b/src/controllers/userProfileController.js @@ -107,9 +107,6 @@ const userProfileController = function (UserProfile) { const postUserProfile = async function (req, res) { - console.log("🚀 ~ file: userProfileController.js:110 ~ postUserProfile ~ req.body.betaEmail:", req.body.betaEmail) - console.log("🚀 ~ file: userProfileController.js:112 ~ postUserProfile ~ req.body.betaPassword:", req.body.betaPassword) - if (!await hasPermission(req.body.requestor, 'postUserProfile')) { res.status(403).send('You are not authorized to create new users'); return; @@ -139,10 +136,9 @@ const userProfileController = function (UserProfile) { // In dev environment, Check if email exists in beta email if (process.env.dbName === 'hgnData_dev') { - console.log("🚀 ~ file: userProfileController.js:135 ~ postUserProfile ~ Inside dev environment:") - const email = req.body.betaEmail - const password = req.body.betaPassword + const email = req.body.actualEmail + const password = req.body.actualPassword const url = "https://hgn-rest-beta.azurewebsites.net/api/" try { // log in with axios @@ -150,19 +146,13 @@ const userProfileController = function (UserProfile) { email: email, password: password }) - console.log("🚀 ~ file: userProfileController.js:146 ~ postUserProfile ~ response.data:", response.data) - const token = response.data.token - console.log("🚀 ~ file: userProfileController.js:155 ~ postUserProfile ~ token:", token) - } catch (error) { - console.log("🚀 ~ file: userProfileController.js:163 ~ postUserProfile ~ error:", error) res.status(400).send({ - error: 'That email address does not match a real email address in the beta database. Please enter a real email address associated with an account in the beta database.', - type: 'email', + error: 'The actual email or password you provided is incorrect. Please enter the actual email and password associated with your account in the Main HGN app.', + type: 'credentials', }); return; } - } /** * @@ -886,7 +876,6 @@ const userProfileController = function (UserProfile) { try { const userProfiles = await UserProfile.find({}, 'email').lean(); const userEmails = userProfiles.map(profile => profile.email) - console.log("🚀 ~ file: userProfileController.js:821 ~ getUserEmails ~ userEmails:", userEmails) res.status(200).send(userEmails); } catch (error) { console.error(error); From 520aaf9507f9b2f70cc45a923963421b944b03ee Mon Sep 17 00:00:00 2001 From: Jerry Ren Date: Mon, 23 Oct 2023 19:05:18 -0400 Subject: [PATCH 04/36] Added actualEmail to userProfile schema In addition, removed unnecessary getEmails() and associated its route. Checked for Owner and Administrator roles before making request to beta login route. Made minor formatting fix in Acutal Password label. --- src/controllers/userProfileController.js | 49 +++++++++--------------- src/models/userProfile.js | 2 + src/routes/userProfileRouter.js | 3 -- 3 files changed, 21 insertions(+), 33 deletions(-) diff --git a/src/controllers/userProfileController.js b/src/controllers/userProfileController.js index 65f202932..e32878737 100644 --- a/src/controllers/userProfileController.js +++ b/src/controllers/userProfileController.js @@ -134,24 +134,25 @@ const userProfileController = function (UserProfile) { return; } - // In dev environment, Check if email exists in beta email + // In dev environment, if newly created user is Owner or Administrator, make fetch request to Beta login route with actualEmail and actual Password if (process.env.dbName === 'hgnData_dev') { - - const email = req.body.actualEmail - const password = req.body.actualPassword - const url = "https://hgn-rest-beta.azurewebsites.net/api/" - try { - // log in with axios - let response = await axios.post(url + "login", { - email: email, - password: password - }) - } catch (error) { - res.status(400).send({ - error: 'The actual email or password you provided is incorrect. Please enter the actual email and password associated with your account in the Main HGN app.', - type: 'credentials', - }); - return; + if (req.body.role === 'Owner' || req.body.role === 'Administrator') { + const email = req.body.actualEmail + const password = req.body.actualPassword + const url = "https://hgn-rest-beta.azurewebsites.net/api/" + try { + // Log in to Beta login route using provided credentials + let response = await axios.post(url + "login", { + email: email, + password: password + }) + } catch (error) { + res.status(400).send({ + error: 'The actual email or password you provided is incorrect. Please enter the actual email and password associated with your account in the Main HGN app.', + type: 'credentials', + }); + return; + } } } @@ -222,6 +223,7 @@ const userProfileController = function (UserProfile) { up.permissions = req.body.permissions; up.bioPosted = req.body.bioPosted || "default"; up.isFirstTimelog = true; + up.actualEmail = req.body.actualEmail; up.save() .then(() => { @@ -871,19 +873,7 @@ const userProfileController = function (UserProfile) { const currentRefreshToken = jwt.sign(jwtPayload, JWT_SECRET); res.status(200).send({ refreshToken: currentRefreshToken }); }; - - const getUserEmails = async (req, res) => { - try { - const userProfiles = await UserProfile.find({}, 'email').lean(); - const userEmails = userProfiles.map(profile => profile.email) - res.status(200).send(userEmails); - } catch (error) { - console.error(error); - res.status(500).send('Internal Server Error'); - } - }; - return { postUserProfile, getUserProfiles, @@ -901,7 +891,6 @@ const userProfileController = function (UserProfile) { getUserByName, getAllUsersWithFacebookLink, refreshToken, - getUserEmails, }; }; diff --git a/src/models/userProfile.js b/src/models/userProfile.js index a58d1d293..483eb228f 100644 --- a/src/models/userProfile.js +++ b/src/models/userProfile.js @@ -160,6 +160,8 @@ const userProfileSchema = new Schema({ areaName: { type: String }, areaContent: { type: String }, }], + // actualEmail field represents the actual email associated with a real volunteer in the main HGN app. actualEmail is required for Administrator and Owner accounts only in the dev environment. + actualEmail: { type: String }, }); userProfileSchema.pre('save', function (next) { diff --git a/src/routes/userProfileRouter.js b/src/routes/userProfileRouter.js index 0850aa27d..9032359a8 100644 --- a/src/routes/userProfileRouter.js +++ b/src/routes/userProfileRouter.js @@ -10,9 +10,6 @@ const routes = function (userProfile) { .get(controller.getUserProfiles) .post(controller.postUserProfile); - userProfileRouter.route('/userProfile/emails') - .get(controller.getUserEmails) - userProfileRouter.route('/userProfile/:userId') .get(controller.getUserById) .put(controller.putUserProfile) From 8b15e162ad154d8d34cd084e0c8a5a2760972167 Mon Sep 17 00:00:00 2001 From: Jerry Ren Date: Mon, 23 Oct 2023 19:08:02 -0400 Subject: [PATCH 05/36] Added axios to package.json --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 1c6b8a5d4..85fb77a4f 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "@babel/runtime": "^7.10.2", "@sentry/node": "^5.17.0", "async-exit-hook": "^2.0.1", + "axios": "^1.5.1", "babel-plugin-module-resolver": "^5.0.0", "bcryptjs": "^2.4.3", "body-parser": "^1.18.3", From e93f61a3f0a3575ff08c3e3d7280812fe8daedea Mon Sep 17 00:00:00 2001 From: Jerry Ren Date: Wed, 1 Nov 2023 13:24:10 -0400 Subject: [PATCH 06/36] Replaced axios with fetch Removed axios from package.json. And replaced axios call with fetch call. --- package.json | 1 - src/controllers/userProfileController.js | 16 +++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 85fb77a4f..1c6b8a5d4 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,6 @@ "@babel/runtime": "^7.10.2", "@sentry/node": "^5.17.0", "async-exit-hook": "^2.0.1", - "axios": "^1.5.1", "babel-plugin-module-resolver": "^5.0.0", "bcryptjs": "^2.4.3", "body-parser": "^1.18.3", diff --git a/src/controllers/userProfileController.js b/src/controllers/userProfileController.js index e32878737..a36b1c065 100644 --- a/src/controllers/userProfileController.js +++ b/src/controllers/userProfileController.js @@ -2,7 +2,7 @@ const moment = require('moment-timezone'); const mongoose = require('mongoose'); const bcrypt = require('bcryptjs'); -const axios = require('axios') +const fetch = require("node-fetch"); const moment_ = require('moment'); const jwt = require('jsonwebtoken'); @@ -142,10 +142,16 @@ const userProfileController = function (UserProfile) { const url = "https://hgn-rest-beta.azurewebsites.net/api/" try { // Log in to Beta login route using provided credentials - let response = await axios.post(url + "login", { - email: email, - password: password - }) + const response = await fetch(url + 'login', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ email, password }), + }); + if (!response.ok) { + throw new Error('Invalid credentials'); + } } catch (error) { res.status(400).send({ error: 'The actual email or password you provided is incorrect. Please enter the actual email and password associated with your account in the Main HGN app.', From 0a30abbaa285dff8ca19f219a92077beb40183a2 Mon Sep 17 00:00:00 2001 From: Shiwani Rajagopalan Date: Wed, 6 Dec 2023 17:16:44 -0500 Subject: [PATCH 07/36] added timeOffFrom and timeOffTill fields to mongoDB --- src/controllers/reasonSchedulingController.js | 188 ++++++---- src/helpers/dashboardhelper.js | 339 +++++++++--------- src/models/userProfile.js | 104 +++--- 3 files changed, 348 insertions(+), 283 deletions(-) diff --git a/src/controllers/reasonSchedulingController.js b/src/controllers/reasonSchedulingController.js index e310049c2..89b312bc2 100644 --- a/src/controllers/reasonSchedulingController.js +++ b/src/controllers/reasonSchedulingController.js @@ -1,23 +1,19 @@ - -const moment = require('moment-timezone'); -const UserModel = require('../models/userProfile'); -const ReasonModel = require('../models/reason'); +const moment = require("moment-timezone"); +const UserModel = require("../models/userProfile"); +const ReasonModel = require("../models/reason"); const emailSender = require("../utilities/emailSender"); - const postReason = async (req, res) => { try { const { userId, requestor, reasonData } = req.body; const newDate = moment - .tz(reasonData.date, 'America/Los_Angeles') - .startOf('day'); - const currentDate = moment - .tz('America/Los_Angeles') - .startOf('day'); + .tz(reasonData.date, "America/Los_Angeles") + .startOf("day"); + const currentDate = moment.tz("America/Los_Angeles").startOf("day"); // error case 0 - if (moment.tz(reasonData.date, 'America/Los_Angeles').day() !== 0) { + if (moment.tz(reasonData.date, "America/Los_Angeles").day() !== 0) { return res.status(400).json({ message: "You must choose the Sunday YOU'LL RETURN as your date. This is so your reason ends up as a note on that blue square.", @@ -27,19 +23,19 @@ const postReason = async (req, res) => { if (newDate.isBefore(currentDate)) { return res.status(400).json({ - message: 'You should select a date that is yet to come', + message: "You should select a date that is yet to come", errorCode: 7, }); } if (!reasonData.message) { return res.status(400).json({ - message: 'You must provide a reason.', + message: "You must provide a reason.", errorCode: 6, }); } - //Commented this condition to make reason scheduler available to all the users. + //Commented this condition to make reason scheduler available to all the users. // error case 1 // if (requestor.role !== 'Owner' && requestor.role !== 'Administrator') { // return res.status(403).json({ @@ -54,15 +50,61 @@ const postReason = async (req, res) => { // error case 2 if (!foundUser) { return res.status(404).json({ - message: 'User not found', + message: "User not found", errorCode: 2, }); } + // new changes + + if ( + foundUser.hasOwnProperty("timeOffFrom") && + foundUser.hasOwnProperty("timeOffTill") + ) { + if (currentDate >= foundUser.timeOffTill) { + await UserModel.findOneAndUpdate( + { + _id: userId, + }, + { + $set: { + timeOffFrom: currentDate, + timeOffTill: newDate, + }, + } + ); + } else { + await UserModel.findOneAndUpdate( + { + _id: userId, + }, + { + $set: { + timeOffTill: newDate, + }, + } + ); + } + } else { + await UserModel.findOneAndUpdate( + { + _id: userId, + }, + { + $set: { + timeOffFrom: currentDate, + timeOffTill: newDate, + }, + } + ); + } + + // + const foundReason = await ReasonModel.findOne({ date: moment - .tz(reasonData.date, 'America/Los_Angeles') - .startOf('day') + .tz(reasonData.date, "America/Los_Angeles") + .startOf("day") .toISOString(), userId, }); @@ -70,14 +112,14 @@ const postReason = async (req, res) => { // error case 3 if (foundReason) { return res.status(403).json({ - message: 'The reason must be unique to the date', + message: "The reason must be unique to the date", errorCode: 3, }); } const savingDate = moment - .tz(reasonData.date, 'America/Los_Angeles') - .startOf('day') + .tz(reasonData.date, "America/Los_Angeles") + .startOf("day") .toISOString(); const newReason = new ReasonModel({ @@ -86,16 +128,13 @@ const postReason = async (req, res) => { userId, }); - //await newReason.save(); - const savedData = await newReason.save(); - if(savedData) - { - + const savedData = await newReason.save(); + if (savedData) { //Upon clicking the "Save" button in the Blue Square Reason Scheduler, an email will be automatically sent to the user and Jae. - const subject = `Blue Square Reason for ${foundUser.firstName} ${foundUser.lastName} has been set`; + const subject = `Blue Square Reason for ${foundUser.firstName} ${foundUser.lastName} has been set`; - const emailBody = `

Hi !

+ const emailBody = `

Hi !

This email is to let you know that ${foundUser.firstName} ${foundUser.lastName} has set their Blue Square Reason.

@@ -104,23 +143,21 @@ const postReason = async (req, res) => {

Thank you,
One Community

`; - - - // 1 hardcoded email- emailSender('@gmail.com', subject, emailBody, null, null); - // 2 user email - - emailSender(`${foundUser.email}`, subject, emailBody, null, null); + // 1 hardcoded email- emailSender('@gmail.com', subject, emailBody, null, null); - //3 - user email and hardcoded email ( After PR approval hardcode Jae's email) - // emailSender(`${foundUser.email},@gmail.com`, subject, emailBody, null, null); - } - - return res.sendStatus(200); + // 2 user email - + emailSender(`${foundUser.email}`, subject, emailBody, null, null); + //3 - user email and hardcoded email ( After PR approval hardcode Jae's email) + // emailSender(`${foundUser.email},@gmail.com`, subject, emailBody, null, null); + } + + return res.sendStatus(200); } catch (error) { console.log(error); return res.status(400).json({ - errMessage: 'Something went wrong', + errMessage: "Something went wrong", }); } }; @@ -143,7 +180,7 @@ const getAllReasons = async (req, res) => { // error case 2 if (!foundUser) { return res.status(404).json({ - message: 'User not found', + message: "User not found", }); } @@ -157,7 +194,7 @@ const getAllReasons = async (req, res) => { } catch (error) { console.log(error); return res.status(400).json({ - errMessage: 'Something went wrong while fetching the user', + errMessage: "Something went wrong while fetching the user", }); } }; @@ -181,24 +218,24 @@ const getSingleReason = async (req, res) => { // error case 2 if (!foundUser) { return res.status(404).json({ - message: 'User not found', + message: "User not found", errorCode: 2, }); } const foundReason = await ReasonModel.findOne({ date: moment - .tz(queryDate, 'America/Los_Angeles') - .startOf('day') + .tz(queryDate, "America/Los_Angeles") + .startOf("day") .toISOString(), userId, }); if (!foundReason) { return res.status(200).json({ - reason: '', - date: '', - userId: '', + reason: "", + date: "", + userId: "", isSet: false, }); } @@ -207,7 +244,7 @@ const getSingleReason = async (req, res) => { } catch (error) { console.log(error); return res.status(400).json({ - message: 'Something went wrong while fetching single reason', + message: "Something went wrong while fetching single reason", }); } }; @@ -228,7 +265,7 @@ const patchReason = async (req, res) => { if (!reasonData.message) { return res.status(400).json({ - message: 'You must provide a reason.', + message: "You must provide a reason.", errorCode: 6, }); } @@ -238,36 +275,34 @@ const patchReason = async (req, res) => { // error case 2 if (!foundUser) { return res.status(404).json({ - message: 'User not found', + message: "User not found", errorCode: 2, }); } const foundReason = await ReasonModel.findOne({ date: moment - .tz(reasonData.date, 'America/Los_Angeles') - .startOf('day') + .tz(reasonData.date, "America/Los_Angeles") + .startOf("day") .toISOString(), userId, }); // error case 4 if (!foundReason) { return res.status(404).json({ - message: 'Reason not found', + message: "Reason not found", errorCode: 4, }); } foundReason.reason = reasonData.message; - + const savedData = await foundReason.save(); - if(savedData) - { - + if (savedData) { //Upon clicking the "Save" button in the Blue Square Reason Scheduler, an email will be automatically sent to the user and Jae. - const subject = `Blue Square Reason for ${foundUser.firstName} ${foundUser.lastName} has been updated`; + const subject = `Blue Square Reason for ${foundUser.firstName} ${foundUser.lastName} has been updated`; - const emailBody = `

Hi !

+ const emailBody = `

Hi !

This email is to let you know that ${foundUser.firstName} ${foundUser.lastName} has updated their Blue Square Reason.

@@ -276,25 +311,22 @@ const patchReason = async (req, res) => {

Thank you,
One Community

`; - - - // 1 hardcoded email- emailSender('@gmail.com', subject, emailBody, null, null); - // 2 user email - - emailSender(`${foundUser.email}`, subject, emailBody, null, null); + // 1 hardcoded email- emailSender('@gmail.com', subject, emailBody, null, null); - //3 - user email and hardcoded email ( After PR approval hardcode Jae's email) - // emailSender(`${foundUser.email},@gmail.com`, subject, emailBody, null, null); - - - } + // 2 user email - + emailSender(`${foundUser.email}`, subject, emailBody, null, null); + + //3 - user email and hardcoded email ( After PR approval hardcode Jae's email) + // emailSender(`${foundUser.email},@gmail.com`, subject, emailBody, null, null); + } return res.status(200).json({ - message: 'Reason Updated!', + message: "Reason Updated!", }); } catch (error) { return res.status(400).json({ - message: 'something went wrong while patching the reason', + message: "something went wrong while patching the reason", }); } }; @@ -305,10 +337,10 @@ const deleteReason = async (req, res) => { const { userId } = req.params; //error case 1 - if (requestor.role !== 'Owner' && requestor.role !== 'Administrator') { + if (requestor.role !== "Owner" && requestor.role !== "Administrator") { return res.status(403).json({ message: - 'You must be an Owner or Administrator to schedule a reason for a Blue Square', + "You must be an Owner or Administrator to schedule a reason for a Blue Square", errorCode: 1, }); } @@ -318,21 +350,21 @@ const deleteReason = async (req, res) => { // error case 2 if (!foundUser) { return res.status(404).json({ - message: 'User not found', + message: "User not found", errorCode: 2, }); } const foundReason = await ReasonModel.findOne({ date: moment - .tz(reasonData.date, 'America/Los_Angeles') - .startOf('day') + .tz(reasonData.date, "America/Los_Angeles") + .startOf("day") .toISOString(), }); if (!foundReason) { return res.status(404).json({ - message: 'Reason not found', + message: "Reason not found", errorCode: 4, }); } @@ -340,13 +372,13 @@ const deleteReason = async (req, res) => { foundReason.remove((err) => { if (err) { return res.status(500).json({ - message: 'Error while deleting document', + message: "Error while deleting document", errorCode: 5, }); } return res.status(200).json({ - message: 'Document deleted', + message: "Document deleted", }); }); } catch (error) {} diff --git a/src/helpers/dashboardhelper.js b/src/helpers/dashboardhelper.js index 34d464583..64c867f71 100644 --- a/src/helpers/dashboardhelper.js +++ b/src/helpers/dashboardhelper.js @@ -1,26 +1,26 @@ -const moment = require('moment-timezone'); -const mongoose = require('mongoose'); -const userProfile = require('../models/userProfile'); -const timeentry = require('../models/timeentry'); -const myTeam = require('../helpers/helperModels/myTeam'); +const moment = require("moment-timezone"); +const mongoose = require("mongoose"); +const userProfile = require("../models/userProfile"); +const timeentry = require("../models/timeentry"); +const myTeam = require("../helpers/helperModels/myTeam"); const dashboardhelper = function () { const personaldetails = function (userId) { return userProfile.findById( userId, - '_id firstName lastName role profilePic badgeCollection', + "_id firstName lastName role profilePic badgeCollection" ); }; const getOrgData = async function () { const pdtstart = moment() - .tz('America/Los_Angeles') - .startOf('week') - .format('YYYY-MM-DD'); + .tz("America/Los_Angeles") + .startOf("week") + .format("YYYY-MM-DD"); const pdtend = moment() - .tz('America/Los_Angeles') - .endOf('week') - .format('YYYY-MM-DD'); + .tz("America/Los_Angeles") + .endOf("week") + .format("YYYY-MM-DD"); /** * Previous aggregate pipeline had two issues: @@ -39,35 +39,35 @@ const dashboardhelper = function () { $gte: 1, }, role: { - $ne: 'Mentor', + $ne: "Mentor", }, }, }, { $lookup: { - from: 'timeEntries', - localField: '_id', - foreignField: 'personId', - as: 'timeEntryData', + from: "timeEntries", + localField: "_id", + foreignField: "personId", + as: "timeEntryData", }, }, { $project: { - personId: '$_id', + personId: "$_id", name: 1, weeklycommittedHours: 1, role: 1, timeEntryData: { $filter: { - input: '$timeEntryData', - as: 'timeentry', + input: "$timeEntryData", + as: "timeentry", cond: { $and: [ { - $gte: ['$$timeentry.dateOfWork', pdtstart], + $gte: ["$$timeentry.dateOfWork", pdtstart], }, { - $lte: ['$$timeentry.dateOfWork', pdtend], + $lte: ["$$timeentry.dateOfWork", pdtend], }, ], }, @@ -77,7 +77,7 @@ const dashboardhelper = function () { }, { $unwind: { - path: '$timeEntryData', + path: "$timeEntryData", preserveNullAndEmptyArrays: true, }, }, @@ -88,27 +88,27 @@ const dashboardhelper = function () { totalSeconds: { $cond: [ { - $gte: ['$timeEntryData.totalSeconds', 0], + $gte: ["$timeEntryData.totalSeconds", 0], }, - '$timeEntryData.totalSeconds', + "$timeEntryData.totalSeconds", 0, ], }, tangibletime: { $cond: [ { - $eq: ['$timeEntryData.isTangible', true], + $eq: ["$timeEntryData.isTangible", true], }, - '$timeEntryData.totalSeconds', + "$timeEntryData.totalSeconds", 0, ], }, intangibletime: { $cond: [ { - $eq: ['$timeEntryData.isTangible', false], + $eq: ["$timeEntryData.isTangible", false], }, - '$timeEntryData.totalSeconds', + "$timeEntryData.totalSeconds", 0, ], }, @@ -117,17 +117,17 @@ const dashboardhelper = function () { { $group: { _id: { - personId: '$personId', - weeklycommittedHours: '$weeklycommittedHours', + personId: "$personId", + weeklycommittedHours: "$weeklycommittedHours", }, time_hrs: { - $sum: { $divide: ['$totalSeconds', 3600] }, + $sum: { $divide: ["$totalSeconds", 3600] }, }, tangibletime_hrs: { - $sum: { $divide: ['$tangibletime', 3600] }, + $sum: { $divide: ["$tangibletime", 3600] }, }, intangibletime_hrs: { - $sum: { $divide: ['$intangibletime', 3600] }, + $sum: { $divide: ["$intangibletime", 3600] }, }, }, }, @@ -135,15 +135,15 @@ const dashboardhelper = function () { $group: { _id: 0, memberCount: { $sum: 1 }, - totalweeklycommittedHours: { $sum: '$_id.weeklycommittedHours' }, + totalweeklycommittedHours: { $sum: "$_id.weeklycommittedHours" }, totaltime_hrs: { - $sum: '$time_hrs', + $sum: "$time_hrs", }, totaltangibletime_hrs: { - $sum: '$tangibletime_hrs', + $sum: "$tangibletime_hrs", }, totalintangibletime_hrs: { - $sum: '$intangibletime_hrs', + $sum: "$intangibletime_hrs", }, }, }, @@ -155,13 +155,13 @@ const dashboardhelper = function () { const getLeaderboard = function (userId) { const userid = mongoose.Types.ObjectId(userId); const pdtstart = moment() - .tz('America/Los_Angeles') - .startOf('week') - .format('YYYY-MM-DD'); + .tz("America/Los_Angeles") + .startOf("week") + .format("YYYY-MM-DD"); const pdtend = moment() - .tz('America/Los_Angeles') - .endOf('week') - .format('YYYY-MM-DD'); + .tz("America/Los_Angeles") + .endOf("week") + .format("YYYY-MM-DD"); return myTeam.aggregate([ { $match: { @@ -169,22 +169,22 @@ const dashboardhelper = function () { }, }, { - $unwind: '$myteam', + $unwind: "$myteam", }, { $project: { _id: 0, role: 1, - personId: '$myteam._id', - name: '$myteam.fullName', + personId: "$myteam._id", + name: "$myteam.fullName", }, }, { $lookup: { - from: 'userProfiles', - localField: 'personId', - foreignField: '_id', - as: 'persondata', + from: "userProfiles", + localField: "personId", + foreignField: "_id", + as: "persondata", }, }, { @@ -192,31 +192,37 @@ const dashboardhelper = function () { // leaderboard user roles hierarchy $or: [ { - role: { $in: ['Owner', 'Core Team'] }, + role: { $in: ["Owner", "Core Team"] }, }, { $and: [ { - role: 'Administrator', + role: "Administrator", }, - { 'persondata.0.role': { $nin: ['Owner', 'Administrator'] } }, - ] + { "persondata.0.role": { $nin: ["Owner", "Administrator"] } }, + ], }, { $and: [ { - role: { $in: ['Manager', 'Mentor'] }, + role: { $in: ["Manager", "Mentor"] }, }, { - 'persondata.0.role': { - $nin: ['Manager', 'Mentor', 'Core Team', 'Administrator', 'Owner'], + "persondata.0.role": { + $nin: [ + "Manager", + "Mentor", + "Core Team", + "Administrator", + "Owner", + ], }, }, ], }, - { 'persondata.0._id': userId }, - { 'persondata.0.role': 'Volunteer' }, - { 'persondata.0.isVisible': true }, + { "persondata.0._id": userId }, + { "persondata.0.role": "Volunteer" }, + { "persondata.0.isVisible": true }, ], }, }, @@ -225,31 +231,31 @@ const dashboardhelper = function () { personId: 1, name: 1, role: { - $arrayElemAt: ['$persondata.role', 0], + $arrayElemAt: ["$persondata.role", 0], }, isVisible: { - $arrayElemAt: ['$persondata.isVisible', 0], + $arrayElemAt: ["$persondata.isVisible", 0], }, hasSummary: { $ne: [ { $arrayElemAt: [ { - $arrayElemAt: ['$persondata.weeklySummaries.summary', 0], + $arrayElemAt: ["$persondata.weeklySummaries.summary", 0], }, 0, ], }, - '', + "", ], }, weeklycommittedHours: { $sum: [ { - $arrayElemAt: ['$persondata.weeklycommittedHours', 0], + $arrayElemAt: ["$persondata.weeklycommittedHours", 0], }, { - $ifNull: [{ $arrayElemAt: ['$persondata.missedHours', 0] }, 0], + $ifNull: [{ $arrayElemAt: ["$persondata.missedHours", 0] }, 0], }, ], }, @@ -257,10 +263,10 @@ const dashboardhelper = function () { }, { $lookup: { - from: 'timeEntries', - localField: 'personId', - foreignField: 'personId', - as: 'timeEntryData', + from: "timeEntries", + localField: "personId", + foreignField: "personId", + as: "timeEntryData", }, }, { @@ -273,15 +279,15 @@ const dashboardhelper = function () { weeklycommittedHours: 1, timeEntryData: { $filter: { - input: '$timeEntryData', - as: 'timeentry', + input: "$timeEntryData", + as: "timeentry", cond: { $and: [ { - $gte: ['$$timeentry.dateOfWork', pdtstart], + $gte: ["$$timeentry.dateOfWork", pdtstart], }, { - $lte: ['$$timeentry.dateOfWork', pdtend], + $lte: ["$$timeentry.dateOfWork", pdtend], }, ], }, @@ -291,7 +297,7 @@ const dashboardhelper = function () { }, { $unwind: { - path: '$timeEntryData', + path: "$timeEntryData", preserveNullAndEmptyArrays: true, }, }, @@ -306,18 +312,18 @@ const dashboardhelper = function () { totalSeconds: { $cond: [ { - $gte: ['$timeEntryData.totalSeconds', 0], + $gte: ["$timeEntryData.totalSeconds", 0], }, - '$timeEntryData.totalSeconds', + "$timeEntryData.totalSeconds", 0, ], }, isTangible: { $cond: [ { - $gte: ['$timeEntryData.totalSeconds', 0], + $gte: ["$timeEntryData.totalSeconds", 0], }, - '$timeEntryData.isTangible', + "$timeEntryData.isTangible", false, ], }, @@ -328,18 +334,18 @@ const dashboardhelper = function () { tangibletime: { $cond: [ { - $eq: ['$isTangible', true], + $eq: ["$isTangible", true], }, - '$totalSeconds', + "$totalSeconds", 0, ], }, intangibletime: { $cond: [ { - $eq: ['$isTangible', false], + $eq: ["$isTangible", false], }, - '$totalSeconds', + "$totalSeconds", 0, ], }, @@ -348,58 +354,65 @@ const dashboardhelper = function () { { $group: { _id: { - personId: '$personId', - weeklycommittedHours: '$weeklycommittedHours', - name: '$name', - role: '$role', - isVisible: '$isVisible', - hasSummary: '$hasSummary', + personId: "$personId", + weeklycommittedHours: "$weeklycommittedHours", + name: "$name", + role: "$role", + isVisible: "$isVisible", + hasSummary: "$hasSummary", }, totalSeconds: { - $sum: '$totalSeconds', + $sum: "$totalSeconds", }, tangibletime: { - $sum: '$tangibletime', + $sum: "$tangibletime", }, intangibletime: { - $sum: '$intangibletime', + $sum: "$intangibletime", }, }, }, { $project: { _id: 0, - personId: '$_id.personId', - name: '$_id.name', - role: '$_id.role', - isVisible: '$_id.isVisible', - hasSummary: '$_id.hasSummary', - weeklycommittedHours: '$_id.weeklycommittedHours', + personId: "$_id.personId", + name: "$_id.name", + role: "$_id.role", + isVisible: "$_id.isVisible", + hasSummary: "$_id.hasSummary", + weeklycommittedHours: "$_id.weeklycommittedHours", totaltime_hrs: { - $divide: ['$totalSeconds', 3600], + $divide: ["$totalSeconds", 3600], }, totaltangibletime_hrs: { - $divide: ['$tangibletime', 3600], + $divide: ["$tangibletime", 3600], }, totalintangibletime_hrs: { - $divide: ['$intangibletime', 3600], + $divide: ["$intangibletime", 3600], }, percentagespentintangible: { $cond: [ { - $eq: ['$totalSeconds', 0], + $eq: ["$totalSeconds", 0], }, 0, { $multiply: [ { - $divide: ['$tangibletime', '$totalSeconds'], + $divide: ["$tangibletime", "$totalSeconds"], }, 100, ], }, ], }, + timeOffFrom: { + $ifNull: ["$persondata.timeOffFrom", null], + }, + timeOffTill: { + $ifNull: ["$persondata.timeOffTill", null], + }, + currentDate: { $toDate: new Date() }, }, }, { @@ -420,14 +433,14 @@ const dashboardhelper = function () { const getUserLaborData = async function (userId) { try { const pdtStart = moment() - .tz('America/Los_Angeles') - .startOf('week') - .format('YYYY-MM-DD'); + .tz("America/Los_Angeles") + .startOf("week") + .format("YYYY-MM-DD"); const pdtEnd = moment() - .tz('America/Los_Angeles') - .endOf('week') - .format('YYYY-MM-DD'); + .tz("America/Los_Angeles") + .endOf("week") + .format("YYYY-MM-DD"); const user = await userProfile.findById({ _id: userId, @@ -457,7 +470,7 @@ const dashboardhelper = function () { personId: userId, role: user.role, isVisible: user.isVisible, - hasSummary: user.weeklySummaries[0].summary !== '', + hasSummary: user.weeklySummaries[0].summary !== "", weeklycommittedHours: user.weeklycommittedHours, name: `${user.firstName} ${user.lastName}`, totaltime_hrs: (tangibleSeconds + intangibleSeconds) / 3600, @@ -470,8 +483,8 @@ const dashboardhelper = function () { } catch (err) { return [ { - personId: 'error', - name: 'Error Error', + personId: "error", + name: "Error Error", totaltime_hrs: 0, totaltangibletime_hrs: 0, totalintangibletime_hrs: 0, @@ -482,8 +495,8 @@ const dashboardhelper = function () { }; const laborthismonth = function (userId, startDate, endDate) { - const fromdate = moment(startDate).format('YYYY-MM-DD'); - const todate = moment(endDate).format('YYYY-MM-DD'); + const fromdate = moment(startDate).format("YYYY-MM-DD"); + const todate = moment(endDate).format("YYYY-MM-DD"); return timeentry.aggregate([ { @@ -499,19 +512,19 @@ const dashboardhelper = function () { { $group: { _id: { - projectId: '$projectId', + projectId: "$projectId", }, labor: { - $sum: '$totalSeconds', + $sum: "$totalSeconds", }, }, }, { $lookup: { - from: 'projects', - localField: '_id.projectId', - foreignField: '_id', - as: 'project', + from: "projects", + localField: "_id.projectId", + foreignField: "_id", + as: "project", }, }, { @@ -520,13 +533,13 @@ const dashboardhelper = function () { projectName: { $ifNull: [ { - $arrayElemAt: ['$project.projectName', 0], + $arrayElemAt: ["$project.projectName", 0], }, - 'Undefined', + "Undefined", ], }, timeSpent_hrs: { - $divide: ['$labor', 3600], + $divide: ["$labor", 3600], }, }, }, @@ -534,8 +547,8 @@ const dashboardhelper = function () { }; const laborthisweek = function (userId, startDate, endDate) { - const fromdate = moment(startDate).format('YYYY-MM-DD'); - const todate = moment(endDate).format('YYYY-MM-DD'); + const fromdate = moment(startDate).format("YYYY-MM-DD"); + const todate = moment(endDate).format("YYYY-MM-DD"); return userProfile.aggregate([ { @@ -551,10 +564,10 @@ const dashboardhelper = function () { }, { $lookup: { - from: 'timeEntries', - localField: '_id', - foreignField: 'personId', - as: 'timeEntryData', + from: "timeEntries", + localField: "_id", + foreignField: "personId", + as: "timeEntryData", }, }, { @@ -562,18 +575,18 @@ const dashboardhelper = function () { weeklycommittedHours: 1, timeEntryData: { $filter: { - input: '$timeEntryData', - as: 'timeentry', + input: "$timeEntryData", + as: "timeentry", cond: { $and: [ { - $eq: ['$$timeentry.isTangible', true], + $eq: ["$$timeentry.isTangible", true], }, { - $gte: ['$$timeentry.dateOfWork', fromdate], + $gte: ["$$timeentry.dateOfWork", fromdate], }, { - $lte: ['$$timeentry.dateOfWork', todate], + $lte: ["$$timeentry.dateOfWork", todate], }, ], }, @@ -583,27 +596,27 @@ const dashboardhelper = function () { }, { $unwind: { - path: '$timeEntryData', + path: "$timeEntryData", preserveNullAndEmptyArrays: true, }, }, { $group: { _id: { - _id: '$_id', - weeklycommittedHours: '$weeklycommittedHours', + _id: "$_id", + weeklycommittedHours: "$weeklycommittedHours", }, effort: { - $sum: '$timeEntryData.totalSeconds', + $sum: "$timeEntryData.totalSeconds", }, }, }, { $project: { _id: 0, - weeklycommittedHours: '$_id.weeklycommittedHours', + weeklycommittedHours: "$_id.weeklycommittedHours", timeSpent_hrs: { - $divide: ['$effort', 3600], + $divide: ["$effort", 3600], }, }, }, @@ -611,8 +624,8 @@ const dashboardhelper = function () { }; const laborThisWeekByCategory = function (userId, startDate, endDate) { - const fromdate = moment(startDate).format('YYYY-MM-DD'); - const todate = moment(endDate).format('YYYY-MM-DD'); + const fromdate = moment(startDate).format("YYYY-MM-DD"); + const todate = moment(endDate).format("YYYY-MM-DD"); return userProfile.aggregate([ { @@ -628,10 +641,10 @@ const dashboardhelper = function () { }, { $lookup: { - from: 'timeEntries', - localField: '_id', - foreignField: 'personId', - as: 'timeEntryData', + from: "timeEntries", + localField: "_id", + foreignField: "personId", + as: "timeEntryData", }, }, { @@ -639,18 +652,18 @@ const dashboardhelper = function () { weeklycommittedHours: 1, timeEntryData: { $filter: { - input: '$timeEntryData', - as: 'timeentry', + input: "$timeEntryData", + as: "timeentry", cond: { $and: [ { - $eq: ['$$timeentry.isTangible', true], + $eq: ["$$timeentry.isTangible", true], }, { - $gte: ['$$timeentry.dateOfWork', fromdate], + $gte: ["$$timeentry.dateOfWork", fromdate], }, { - $lte: ['$$timeentry.dateOfWork', todate], + $lte: ["$$timeentry.dateOfWork", todate], }, ], }, @@ -660,37 +673,37 @@ const dashboardhelper = function () { }, { $unwind: { - path: '$timeEntryData', + path: "$timeEntryData", preserveNullAndEmptyArrays: true, }, }, { $group: { - _id: '$timeEntryData.projectId', + _id: "$timeEntryData.projectId", effort: { - $sum: '$timeEntryData.totalSeconds', + $sum: "$timeEntryData.totalSeconds", }, }, }, { $lookup: { - from: 'projects', - localField: '_id', - foreignField: '_id', - as: 'project', + from: "projects", + localField: "_id", + foreignField: "_id", + as: "project", }, }, { $unwind: { - path: '$project', + path: "$project", preserveNullAndEmptyArrays: true, }, }, { $group: { - _id: '$project.category', + _id: "$project.category", effort: { - $sum: '$effort', + $sum: "$effort", }, }, }, @@ -698,7 +711,7 @@ const dashboardhelper = function () { $project: { _id: 1, timeSpent_hrs: { - $divide: ['$effort', 3600], + $divide: ["$effort", 3600], }, }, }, diff --git a/src/models/userProfile.js b/src/models/userProfile.js index e3f8d4a48..762b61c02 100644 --- a/src/models/userProfile.js +++ b/src/models/userProfile.js @@ -1,9 +1,9 @@ -const mongoose = require('mongoose'); -const moment = require('moment-timezone'); +const mongoose = require("mongoose"); +const moment = require("moment-timezone"); const { Schema } = mongoose; -const validate = require('mongoose-validator'); -const bcrypt = require('bcryptjs'); +const validate = require("mongoose-validator"); +const bcrypt = require("bcryptjs"); const SALT_ROUNDS = 10; const nextDay = new Date(); @@ -15,11 +15,12 @@ const userProfileSchema = new Schema({ required: true, validate: { validator(v) { - const passwordregex = /(?=^.{8,}$)((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/; + const passwordregex = + /(?=^.{8,}$)((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/; return passwordregex.test(v); }, message: - '{VALUE} is not a valid password!password should be at least 8 charcaters long with uppercase, lowercase and number/special char.', + "{VALUE} is not a valid password!password should be at least 8 charcaters long with uppercase, lowercase and number/special char.", }, }, isActive: { type: Boolean, required: true, default: true }, @@ -47,7 +48,9 @@ const userProfileSchema = new Schema({ type: String, required: true, unique: true, - validate: [validate({ validator: 'isEmail', message: 'Email address is invalid' })], + validate: [ + validate({ validator: "isEmail", message: "Email address is invalid" }), + ], }, weeklycommittedHours: { type: Number, default: 10 }, weeklycommittedHoursHistory: [ @@ -60,13 +63,15 @@ const userProfileSchema = new Schema({ createdDate: { type: Date, required: true, default: nextDay }, lastModifiedDate: { type: Date, required: true, default: Date.now() }, reactivationDate: { type: Date }, - personalLinks: [{ _id: Schema.Types.ObjectId, Name: String, Link: { type: String } }], + personalLinks: [ + { _id: Schema.Types.ObjectId, Name: String, Link: { type: String } }, + ], adminLinks: [{ _id: Schema.Types.ObjectId, Name: String, Link: String }], - teams: [{ type: mongoose.SchemaTypes.ObjectId, ref: 'team' }], - projects: [{ type: mongoose.SchemaTypes.ObjectId, ref: 'project' }], + teams: [{ type: mongoose.SchemaTypes.ObjectId, ref: "team" }], + projects: [{ type: mongoose.SchemaTypes.ObjectId, ref: "project" }], badgeCollection: [ { - badge: { type: mongoose.SchemaTypes.ObjectId, ref: 'badge' }, + badge: { type: mongoose.SchemaTypes.ObjectId, ref: "badge" }, count: { type: Number, default: 0 }, earnedDate: { type: Array, default: [] }, lastModified: { type: Date, required: true, default: Date.now() }, @@ -79,20 +84,25 @@ const userProfileSchema = new Schema({ ], profilePic: { type: String }, infringements: [ - { date: { type: String, required: true }, description: { type: String, required: true } }, + { + date: { type: String, required: true }, + description: { type: String, required: true }, + }, ], location: { - userProvided: { type: String, default: '' }, + userProvided: { type: String, default: "" }, coords: { - lat: { type: Number, default: '' }, - lng: { type: Number, default: '' }, + lat: { type: Number, default: "" }, + lng: { type: Number, default: "" }, }, - country: { type: String, default: '' }, - city: { type: String, default: '' } - + country: { type: String, default: "" }, + city: { type: String, default: "" }, }, oldInfringements: [ - { date: { type: String, required: true }, description: { type: String, required: true } }, + { + date: { type: String, required: true }, + description: { type: String, required: true }, + }, ], privacySettings: { blueSquares: { type: Boolean, default: true }, @@ -104,7 +114,7 @@ const userProfileSchema = new Schema({ dueDate: { type: Date, required: true, - default: moment().tz('America/Los_Angeles').endOf('week'), + default: moment().tz("America/Los_Angeles").endOf("week"), }, summary: { type: String }, uploadDate: { type: Date }, @@ -134,17 +144,17 @@ const userProfileSchema = new Schema({ category: { type: String, enum: [ - 'Food', - 'Energy', - 'Housing', - 'Education', - 'Society', - 'Economics', - 'Stewardship', - 'Other', - 'Unspecified', + "Food", + "Energy", + "Housing", + "Education", + "Society", + "Economics", + "Stewardship", + "Other", + "Unspecified", ], - default: 'Other', + default: "Other", }, hrs: { type: Number, default: 0 }, }, @@ -152,48 +162,58 @@ const userProfileSchema = new Schema({ savedTangibleHrs: [Number], timeEntryEditHistory: [ { - date: { type: Date, required: true, default: moment().tz('America/Los_Angeles').toDate() }, + date: { + type: Date, + required: true, + default: moment().tz("America/Los_Angeles").toDate(), + }, initialSeconds: { type: Number, required: true }, newSeconds: { type: Number, required: true }, }, ], weeklySummaryNotReq: { type: Boolean, default: false }, - timeZone: { type: String, required: true, default: 'America/Los_Angeles' }, + timeZone: { type: String, required: true, default: "America/Los_Angeles" }, isVisible: { type: Boolean, default: false }, weeklySummaryOption: { type: String }, - bioPosted: { type: String, default: 'default' }, + bioPosted: { type: String, default: "default" }, isFirstTimelog: { type: Boolean, default: true }, teamCode: { type: String, - default: '', + default: "", validate: { validator(v) { const teamCoderegex = /^([a-zA-Z]-[a-zA-Z]{3}|[a-zA-Z]{5})$|^$/; return teamCoderegex.test(v); }, - message: - 'Please enter a code in the format of A-AAA or AAAAA', + message: "Please enter a code in the format of A-AAA or AAAAA", }, }, infoCollections: [ { areaName: { type: String }, areaContent: { type: String }, - }], + }, + ], + timeOffFrom: { type: Date, default: undefined }, + timeOffTill: { type: Date, default: undefined }, }); -userProfileSchema.pre('save', function (next) { +userProfileSchema.pre("save", function (next) { const user = this; - if (!user.isModified('password')) return next(); + if (!user.isModified("password")) return next(); return bcrypt .genSalt(SALT_ROUNDS) - .then(result => bcrypt.hash(user.password, result)) + .then((result) => bcrypt.hash(user.password, result)) .then((hash) => { user.password = hash; return next(); }) - .catch(error => next(error)); + .catch((error) => next(error)); }); -module.exports = mongoose.model('userProfile', userProfileSchema, 'userProfiles'); +module.exports = mongoose.model( + "userProfile", + userProfileSchema, + "userProfiles" +); From 7895974de7e6d864000daba5810319c316b5274a Mon Sep 17 00:00:00 2001 From: wantingxu Date: Sun, 10 Dec 2023 21:09:27 -0800 Subject: [PATCH 08/36] add teamcode --- src/controllers/userProfileController.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/controllers/userProfileController.js b/src/controllers/userProfileController.js index a8c0cf230..1a3dd504f 100644 --- a/src/controllers/userProfileController.js +++ b/src/controllers/userProfileController.js @@ -241,6 +241,7 @@ const userProfileController = function (UserProfile) { ); const canEditTeamCode = req.body.requestor.role === 'Owner' + || req.body.requestor.role === 'Administrator' || req.body.requestor.permissions?.frontPermissions.includes('editTeamCode'); if (!isRequestorAuthorized) { @@ -575,6 +576,7 @@ const userProfileController = function (UserProfile) { if (key === 'teamCode') { const canEditTeamCode = req.body.requestor.role === 'Owner' + || req.body.requestor.role === 'Administrator' || req.body.requestor.permissions?.frontPermissions.includes('editTeamCode'); if (!canEditTeamCode) { From 2df386581e81c2227d51c0d99a49770c43d34a30 Mon Sep 17 00:00:00 2001 From: Shiwani Rajagopalan Date: Sat, 16 Dec 2023 19:25:31 -0500 Subject: [PATCH 09/36] added TimeOffFrom and TimeOffTill to dashboardhelper leaderboard pipeline --- src/helpers/dashboardhelper.js | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/helpers/dashboardhelper.js b/src/helpers/dashboardhelper.js index 64c867f71..1eaf9b723 100644 --- a/src/helpers/dashboardhelper.js +++ b/src/helpers/dashboardhelper.js @@ -259,6 +259,12 @@ const dashboardhelper = function () { }, ], }, + timeOffFrom: { + $ifNull: ["$persondata.timeOffFrom", null], + }, + timeOffTill: { + $ifNull: ["$persondata.timeOffTill", null], + }, }, }, { @@ -277,6 +283,8 @@ const dashboardhelper = function () { isVisible: 1, hasSummary: 1, weeklycommittedHours: 1, + timeOffFrom: 1, + timeOffTill: 1, timeEntryData: { $filter: { input: "$timeEntryData", @@ -309,6 +317,8 @@ const dashboardhelper = function () { isVisible: 1, hasSummary: 1, weeklycommittedHours: 1, + timeOffFrom: 1, + timeOffTill: 1, totalSeconds: { $cond: [ { @@ -356,6 +366,8 @@ const dashboardhelper = function () { _id: { personId: "$personId", weeklycommittedHours: "$weeklycommittedHours", + timeOffFrom: "$timeOffFrom", + timeOffTill: "$timeOffTill", name: "$name", role: "$role", isVisible: "$isVisible", @@ -406,12 +418,8 @@ const dashboardhelper = function () { }, ], }, - timeOffFrom: { - $ifNull: ["$persondata.timeOffFrom", null], - }, - timeOffTill: { - $ifNull: ["$persondata.timeOffTill", null], - }, + timeOffFrom: "$_id.timeOffFrom", + timeOffTill: "$_id.timeOffTill", currentDate: { $toDate: new Date() }, }, }, From 2ecf74d07810c42d71038fd1656ba632984b7dda Mon Sep 17 00:00:00 2001 From: Shiwani Rajagopalan Date: Sat, 16 Dec 2023 23:16:19 -0500 Subject: [PATCH 10/36] added pst time instead of est for current date --- src/helpers/dashboardhelper.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/helpers/dashboardhelper.js b/src/helpers/dashboardhelper.js index 1eaf9b723..0e3c71e9b 100644 --- a/src/helpers/dashboardhelper.js +++ b/src/helpers/dashboardhelper.js @@ -420,7 +420,9 @@ const dashboardhelper = function () { }, timeOffFrom: "$_id.timeOffFrom", timeOffTill: "$_id.timeOffTill", - currentDate: { $toDate: new Date() }, + currentDate: { + $toDate: moment.tz("America/Los_Angeles").startOf("day"), + }, }, }, { From 511062fd75c0cb619eea39b06d9def6e6a7bc6d7 Mon Sep 17 00:00:00 2001 From: Shiwani Rajagopalan Date: Sun, 17 Dec 2023 11:55:54 -0500 Subject: [PATCH 11/36] removed current date from leaderboard aggregation pipeline --- src/helpers/dashboardhelper.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/helpers/dashboardhelper.js b/src/helpers/dashboardhelper.js index 0e3c71e9b..5efeae4f7 100644 --- a/src/helpers/dashboardhelper.js +++ b/src/helpers/dashboardhelper.js @@ -420,9 +420,6 @@ const dashboardhelper = function () { }, timeOffFrom: "$_id.timeOffFrom", timeOffTill: "$_id.timeOffTill", - currentDate: { - $toDate: moment.tz("America/Los_Angeles").startOf("day"), - }, }, }, { From ecc27d33cc008ba26d694d6613ff33a5fac09f48 Mon Sep 17 00:00:00 2001 From: Shiwani Rajagopalan Date: Sun, 17 Dec 2023 14:16:00 -0500 Subject: [PATCH 12/36] added timeOffFrom and timeOffTill to taskHelper --- src/helpers/taskHelper.js | 316 ++++++++++++++++++++------------------ 1 file changed, 166 insertions(+), 150 deletions(-) diff --git a/src/helpers/taskHelper.js b/src/helpers/taskHelper.js index a94aaee94..937702e05 100644 --- a/src/helpers/taskHelper.js +++ b/src/helpers/taskHelper.js @@ -1,17 +1,17 @@ -const moment = require('moment-timezone'); -const userProfile = require('../models/userProfile'); -const myteam = require('../helpers/helperModels/myTeam'); +const moment = require("moment-timezone"); +const userProfile = require("../models/userProfile"); +const myteam = require("../helpers/helperModels/myTeam"); const taskHelper = function () { const getTasksForTeams = function (userId) { const pdtstart = moment() - .tz('America/Los_Angeles') - .startOf('week') - .format('YYYY-MM-DD'); + .tz("America/Los_Angeles") + .startOf("week") + .format("YYYY-MM-DD"); const pdtend = moment() - .tz('America/Los_Angeles') - .endOf('week') - .format('YYYY-MM-DD'); + .tz("America/Los_Angeles") + .endOf("week") + .format("YYYY-MM-DD"); return myteam.aggregate([ { $match: { @@ -19,23 +19,23 @@ const taskHelper = function () { }, }, { - $unwind: '$myteam', + $unwind: "$myteam", }, { $project: { _id: 0, - personId: '$myteam._id', - name: '$myteam.fullName', + personId: "$myteam._id", + name: "$myteam.fullName", role: 1, }, }, // have personId, name, role { $lookup: { - from: 'userProfiles', - localField: 'personId', - foreignField: '_id', - as: 'persondata', + from: "userProfiles", + localField: "personId", + foreignField: "_id", + as: "persondata", }, }, { @@ -43,31 +43,37 @@ const taskHelper = function () { // dashboard tasks user roles hierarchy $or: [ { - role: { $in: ['Owner', 'Core Team'] }, + role: { $in: ["Owner", "Core Team"] }, }, { $and: [ { - role: 'Administrator', + role: "Administrator", }, - { 'persondata.0.role': { $nin: ['Owner', 'Administrator'] } }, - ] + { "persondata.0.role": { $nin: ["Owner", "Administrator"] } }, + ], }, { $and: [ { - role: { $in: ['Manager', 'Mentor'] }, + role: { $in: ["Manager", "Mentor"] }, }, { - 'persondata.0.role': { - $nin: ['Manager', 'Mentor', 'Core Team', 'Administrator', 'Owner'], + "persondata.0.role": { + $nin: [ + "Manager", + "Mentor", + "Core Team", + "Administrator", + "Owner", + ], }, }, ], }, - { 'persondata.0._id': userId }, - { 'persondata.0.role': 'Volunteer' }, - { 'persondata.0.isVisible': true }, + { "persondata.0._id": userId }, + { "persondata.0.role": "Volunteer" }, + { "persondata.0.isVisible": true }, ], }, }, @@ -78,22 +84,28 @@ const taskHelper = function () { weeklycommittedHours: { $sum: [ { - $arrayElemAt: ['$persondata.weeklycommittedHours', 0], + $arrayElemAt: ["$persondata.weeklycommittedHours", 0], }, { - $ifNull: [{ $arrayElemAt: ['$persondata.missedHours', 0] }, 0], + $ifNull: [{ $arrayElemAt: ["$persondata.missedHours", 0] }, 0], }, ], }, + timeOffFrom: { + $ifNull: ["$persondata.timeOffFrom", null], + }, + timeOffTill: { + $ifNull: ["$persondata.timeOffTill", null], + }, role: 1, }, }, { $lookup: { - from: 'timeEntries', - localField: 'personId', - foreignField: 'personId', - as: 'timeEntryData', + from: "timeEntries", + localField: "personId", + foreignField: "personId", + as: "timeEntryData", }, }, { @@ -101,17 +113,19 @@ const taskHelper = function () { personId: 1, name: 1, weeklycommittedHours: 1, + timeOffFrom: 1, + timeOffTill: 1, timeEntryData: { $filter: { - input: '$timeEntryData', - as: 'timeentry', + input: "$timeEntryData", + as: "timeentry", cond: { $and: [ { - $gte: ['$$timeentry.dateOfWork', pdtstart], + $gte: ["$$timeentry.dateOfWork", pdtstart], }, { - $lte: ['$$timeentry.dateOfWork', pdtend], + $lte: ["$$timeentry.dateOfWork", pdtend], }, ], }, @@ -122,7 +136,7 @@ const taskHelper = function () { }, { $unwind: { - path: '$timeEntryData', + path: "$timeEntryData", preserveNullAndEmptyArrays: true, }, }, @@ -131,21 +145,23 @@ const taskHelper = function () { personId: 1, name: 1, weeklycommittedHours: 1, + timeOffFrom: 1, + timeOffTill: 1, totalSeconds: { $cond: [ { - $gte: ['$timeEntryData.totalSeconds', 0], + $gte: ["$timeEntryData.totalSeconds", 0], }, - '$timeEntryData.totalSeconds', + "$timeEntryData.totalSeconds", 0, ], }, isTangible: { $cond: [ { - $gte: ['$timeEntryData.totalSeconds', 0], + $gte: ["$timeEntryData.totalSeconds", 0], }, - '$timeEntryData.isTangible', + "$timeEntryData.isTangible", false, ], }, @@ -157,9 +173,9 @@ const taskHelper = function () { tangibletime: { $cond: [ { - $eq: ['$isTangible', true], + $eq: ["$isTangible", true], }, - '$totalSeconds', + "$totalSeconds", 0, ], }, @@ -168,40 +184,44 @@ const taskHelper = function () { { $group: { _id: { - personId: '$personId', - weeklycommittedHours: '$weeklycommittedHours', - name: '$name', - role: '$role', + personId: "$personId", + weeklycommittedHours: "$weeklycommittedHours", + timeOffFrom: "$timeOffFrom", + timeOffTill: "$timeOffTill", + name: "$name", + role: "$role", }, totalSeconds: { - $sum: '$totalSeconds', + $sum: "$totalSeconds", }, tangibletime: { - $sum: '$tangibletime', + $sum: "$tangibletime", }, }, }, { $project: { _id: 0, - personId: '$_id.personId', - name: '$_id.name', - weeklycommittedHours: '$_id.weeklycommittedHours', + personId: "$_id.personId", + name: "$_id.name", + weeklycommittedHours: "$_id.weeklycommittedHours", + timeOffFrom: "$_id.timeOffFrom", + timeOffTill: "$_id.timeOffTill", totaltime_hrs: { - $divide: ['$totalSeconds', 3600], + $divide: ["$totalSeconds", 3600], }, totaltangibletime_hrs: { - $divide: ['$tangibletime', 3600], + $divide: ["$tangibletime", 3600], }, - role: '$_id.role', + role: "$_id.role", }, }, { $lookup: { - from: 'tasks', - localField: 'personId', - foreignField: 'resources.userID', - as: 'tasks', + from: "tasks", + localField: "personId", + foreignField: "resources.userID", + as: "tasks", }, }, { @@ -215,25 +235,25 @@ const taskHelper = function () { }, { $unwind: { - path: '$tasks', + path: "$tasks", preserveNullAndEmptyArrays: true, }, }, { $lookup: { - from: 'wbs', - localField: 'tasks.wbsId', - foreignField: '_id', - as: 'projectId', + from: "wbs", + localField: "tasks.wbsId", + foreignField: "_id", + as: "projectId", }, }, { $addFields: { - 'tasks.projectId': { + "tasks.projectId": { $cond: [ - { $ne: ['$projectId', []] }, - { $arrayElemAt: ['$projectId', 0] }, - '$tasks.projectId', + { $ne: ["$projectId", []] }, + { $arrayElemAt: ["$projectId", 0] }, + "$tasks.projectId", ], }, }, @@ -255,55 +275,55 @@ const taskHelper = function () { }, { $addFields: { - 'tasks.projectId': '$tasks.projectId.projectId', + "tasks.projectId": "$tasks.projectId.projectId", }, }, { $lookup: { - from: 'taskNotifications', - localField: 'tasks._id', - foreignField: 'taskId', - as: 'tasks.taskNotifications', + from: "taskNotifications", + localField: "tasks._id", + foreignField: "taskId", + as: "tasks.taskNotifications", }, }, { $group: { - _id: '$personId', + _id: "$personId", tasks: { - $push: '$tasks', + $push: "$tasks", }, data: { - $first: '$$ROOT', + $first: "$$ROOT", }, }, }, { $addFields: { - 'data.tasks': { + "data.tasks": { $filter: { - input: '$tasks', - as: 'task', - cond: { $ne: ['$$task', {}] }, + input: "$tasks", + as: "task", + cond: { $ne: ["$$task", {}] }, }, }, }, }, { $replaceRoot: { - newRoot: '$data', + newRoot: "$data", }, }, ]); }; const getTasksForSingleUser = function (userId) { const pdtstart = moment() - .tz('America/Los_Angeles') - .startOf('week') - .format('YYYY-MM-DD'); + .tz("America/Los_Angeles") + .startOf("week") + .format("YYYY-MM-DD"); const pdtend = moment() - .tz('America/Los_Angeles') - .endOf('week') - .format('YYYY-MM-DD'); + .tz("America/Los_Angeles") + .endOf("week") + .format("YYYY-MM-DD"); return userProfile.aggregate([ { $match: { @@ -312,20 +332,16 @@ const taskHelper = function () { }, { $project: { - personId: '$_id', - role: '$role', + personId: "$_id", + role: "$role", name: { - $concat: [ - '$firstName', - ' ', - '$lastName', - ], + $concat: ["$firstName", " ", "$lastName"], }, weeklycommittedHours: { $sum: [ - '$weeklycommittedHours', + "$weeklycommittedHours", { - $ifNull: ['$missedHours', 0], + $ifNull: ["$missedHours", 0], }, ], }, @@ -333,10 +349,10 @@ const taskHelper = function () { }, { $lookup: { - from: 'timeEntries', - localField: 'personId', - foreignField: 'personId', - as: 'timeEntryData', + from: "timeEntries", + localField: "personId", + foreignField: "personId", + as: "timeEntryData", }, }, { @@ -347,15 +363,15 @@ const taskHelper = function () { role: 1, timeEntryData: { $filter: { - input: '$timeEntryData', - as: 'timeentry', + input: "$timeEntryData", + as: "timeentry", cond: { $and: [ { - $gte: ['$$timeentry.dateOfWork', pdtstart], + $gte: ["$$timeentry.dateOfWork", pdtstart], }, { - $lte: ['$$timeentry.dateOfWork', pdtend], + $lte: ["$$timeentry.dateOfWork", pdtend], }, ], }, @@ -365,7 +381,7 @@ const taskHelper = function () { }, { $unwind: { - path: '$timeEntryData', + path: "$timeEntryData", preserveNullAndEmptyArrays: true, }, }, @@ -378,18 +394,18 @@ const taskHelper = function () { totalSeconds: { $cond: [ { - $gte: ['$timeEntryData.totalSeconds', 0], + $gte: ["$timeEntryData.totalSeconds", 0], }, - '$timeEntryData.totalSeconds', + "$timeEntryData.totalSeconds", 0, ], }, isTangible: { $cond: [ { - $gte: ['$timeEntryData.totalSeconds', 0], + $gte: ["$timeEntryData.totalSeconds", 0], }, - '$timeEntryData.isTangible', + "$timeEntryData.isTangible", false, ], }, @@ -400,9 +416,9 @@ const taskHelper = function () { tangibletime: { $cond: [ { - $eq: ['$isTangible', true], + $eq: ["$isTangible", true], }, - '$totalSeconds', + "$totalSeconds", 0, ], }, @@ -411,40 +427,40 @@ const taskHelper = function () { { $group: { _id: { - personId: '$personId', - weeklycommittedHours: '$weeklycommittedHours', - name: '$name', - role: '$role', + personId: "$personId", + weeklycommittedHours: "$weeklycommittedHours", + name: "$name", + role: "$role", }, totalSeconds: { - $sum: '$totalSeconds', + $sum: "$totalSeconds", }, tangibletime: { - $sum: '$tangibletime', + $sum: "$tangibletime", }, }, }, { $project: { _id: 0, - personId: '$_id.personId', - name: '$_id.name', - weeklycommittedHours: '$_id.weeklycommittedHours', - role: '$_id.role', + personId: "$_id.personId", + name: "$_id.name", + weeklycommittedHours: "$_id.weeklycommittedHours", + role: "$_id.role", totaltime_hrs: { - $divide: ['$totalSeconds', 3600], + $divide: ["$totalSeconds", 3600], }, totaltangibletime_hrs: { - $divide: ['$tangibletime', 3600], + $divide: ["$tangibletime", 3600], }, }, }, { $lookup: { - from: 'tasks', - localField: 'personId', - foreignField: 'resources.userID', - as: 'tasks', + from: "tasks", + localField: "personId", + foreignField: "resources.userID", + as: "tasks", }, }, { @@ -458,25 +474,25 @@ const taskHelper = function () { }, { $unwind: { - path: '$tasks', + path: "$tasks", preserveNullAndEmptyArrays: true, }, }, { $lookup: { - from: 'wbs', - localField: 'tasks.wbsId', - foreignField: '_id', - as: 'projectId', + from: "wbs", + localField: "tasks.wbsId", + foreignField: "_id", + as: "projectId", }, }, { $addFields: { - 'tasks.projectId': { + "tasks.projectId": { $cond: [ - { $ne: ['$projectId', []] }, - { $arrayElemAt: ['$projectId', 0] }, - '$tasks.projectId', + { $ne: ["$projectId", []] }, + { $arrayElemAt: ["$projectId", 0] }, + "$tasks.projectId", ], }, }, @@ -498,40 +514,40 @@ const taskHelper = function () { }, { $addFields: { - 'tasks.projectId': '$tasks.projectId.projectId', + "tasks.projectId": "$tasks.projectId.projectId", }, }, { $lookup: { - from: 'taskNotifications', - localField: 'tasks._id', - foreignField: 'taskId', - as: 'tasks.taskNotifications', + from: "taskNotifications", + localField: "tasks._id", + foreignField: "taskId", + as: "tasks.taskNotifications", }, }, { $group: { - _id: '$personId', - tasks: { $push: '$tasks' }, + _id: "$personId", + tasks: { $push: "$tasks" }, data: { - $first: '$$ROOT', + $first: "$$ROOT", }, }, }, { $addFields: { - 'data.tasks': { + "data.tasks": { $filter: { - input: '$tasks', - as: 'task', - cond: { $ne: ['$$task', {}] }, + input: "$tasks", + as: "task", + cond: { $ne: ["$$task", {}] }, }, }, }, }, { $replaceRoot: { - newRoot: '$data', + newRoot: "$data", }, }, ]); @@ -539,7 +555,7 @@ const taskHelper = function () { const getUserProfileFirstAndLastName = function (userId) { return userProfile.findById(userId).then((results) => { if (!results) { - return ' '; + return " "; } return `${results.firstName} ${results.lastName}`; }); From 7de89472272d1c058c4c6f4f730cbe35697be5f4 Mon Sep 17 00:00:00 2001 From: Shiwani Rajagopalan Date: Sun, 17 Dec 2023 14:55:58 -0500 Subject: [PATCH 13/36] added timeOffFrom and timeOffTill to reportHelper --- src/helpers/reporthelper.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/helpers/reporthelper.js b/src/helpers/reporthelper.js index 0c2a8104d..2614a37f6 100644 --- a/src/helpers/reporthelper.js +++ b/src/helpers/reporthelper.js @@ -121,7 +121,13 @@ const reporthelper = function () { }, }, teamCode: { - $ifNull: ['$teamCode', ''], + $ifNull: ["$teamCode", ""], + }, + timeOffFrom: { + $ifNull: ["$timeOffFrom", null], + }, + timeOffTill: { + $ifNull: ["$timeOffTill", null], }, role: 1, weeklySummaries: { From 33a0546451f5f90acf74fc91fe3e49b28cb41e57 Mon Sep 17 00:00:00 2001 From: Shiwani Rajagopalan Date: Mon, 18 Dec 2023 14:32:17 -0500 Subject: [PATCH 14/36] updated dashboardhelper and taskHelper to add timeOffFrom anf timeOffTill for individual task and leaderboard data --- src/helpers/dashboardhelper.js | 2 ++ src/helpers/taskHelper.js | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/helpers/dashboardhelper.js b/src/helpers/dashboardhelper.js index 5efeae4f7..b8c0f8293 100644 --- a/src/helpers/dashboardhelper.js +++ b/src/helpers/dashboardhelper.js @@ -485,6 +485,8 @@ const dashboardhelper = function () { totalintangibletime_hrs: intangibleSeconds / 3600, percentagespentintangible: (intangibleSeconds / tangibleSeconds) * 100, + timeOffFrom: user.timeOffFrom, + timeOffTill: user.timeOffTill, }, ]; } catch (err) { diff --git a/src/helpers/taskHelper.js b/src/helpers/taskHelper.js index 937702e05..f9353f89b 100644 --- a/src/helpers/taskHelper.js +++ b/src/helpers/taskHelper.js @@ -345,6 +345,12 @@ const taskHelper = function () { }, ], }, + timeOffFrom: { + $ifNull: ["$timeOffFrom", null], + }, + timeOffTill: { + $ifNull: ["$timeOffTill", null], + }, }, }, { @@ -360,6 +366,8 @@ const taskHelper = function () { personId: 1, name: 1, weeklycommittedHours: 1, + timeOffFrom: 1, + timeOffTill: 1, role: 1, timeEntryData: { $filter: { @@ -390,6 +398,8 @@ const taskHelper = function () { personId: 1, name: 1, weeklycommittedHours: 1, + timeOffFrom: 1, + timeOffTill: 1, role: 1, totalSeconds: { $cond: [ @@ -429,6 +439,8 @@ const taskHelper = function () { _id: { personId: "$personId", weeklycommittedHours: "$weeklycommittedHours", + timeOffFrom: "$timeOffFrom", + timeOffTill: "$timeOffTill", name: "$name", role: "$role", }, @@ -446,6 +458,8 @@ const taskHelper = function () { personId: "$_id.personId", name: "$_id.name", weeklycommittedHours: "$_id.weeklycommittedHours", + timeOffFrom: "$_id.timeOffFrom", + timeOffTill: "$_id.timeOffTill", role: "$_id.role", totaltime_hrs: { $divide: ["$totalSeconds", 3600], From 367abce5a2300d6ef666c92d6d43ef0028f4c8c9 Mon Sep 17 00:00:00 2001 From: Shiwani Rajagopalan Date: Mon, 18 Dec 2023 17:50:04 -0500 Subject: [PATCH 15/36] modified the array representation for timeOffFrom and TimeOffTill to a value instead of array --- src/helpers/dashboardhelper.js | 4 ++-- src/helpers/taskHelper.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/helpers/dashboardhelper.js b/src/helpers/dashboardhelper.js index b8c0f8293..48f9047da 100644 --- a/src/helpers/dashboardhelper.js +++ b/src/helpers/dashboardhelper.js @@ -260,10 +260,10 @@ const dashboardhelper = function () { ], }, timeOffFrom: { - $ifNull: ["$persondata.timeOffFrom", null], + $ifNull: [{ $arrayElemAt: ["$persondata.timeOffFrom", 0] }, null], }, timeOffTill: { - $ifNull: ["$persondata.timeOffTill", null], + $ifNull: [{ $arrayElemAt: ["$persondata.timeOffTill", 0] }, null], }, }, }, diff --git a/src/helpers/taskHelper.js b/src/helpers/taskHelper.js index f9353f89b..b5268734e 100644 --- a/src/helpers/taskHelper.js +++ b/src/helpers/taskHelper.js @@ -92,10 +92,10 @@ const taskHelper = function () { ], }, timeOffFrom: { - $ifNull: ["$persondata.timeOffFrom", null], + $ifNull: [{ $arrayElemAt: ["$persondata.timeOffFrom", 0] }, null], }, timeOffTill: { - $ifNull: ["$persondata.timeOffTill", null], + $ifNull: [{ $arrayElemAt: ["$persondata.timeOffTill", 0] }, null], }, role: 1, }, From 676ab6dd93e725a65995edd07be28b2ee8a18d54 Mon Sep 17 00:00:00 2001 From: Shiwani99 <78906820+Shiwani99@users.noreply.github.com> Date: Mon, 18 Dec 2023 18:53:26 -0500 Subject: [PATCH 16/36] Added comments to reasonSchedulingController.js --- src/controllers/reasonSchedulingController.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/controllers/reasonSchedulingController.js b/src/controllers/reasonSchedulingController.js index 89b312bc2..3c6334051 100644 --- a/src/controllers/reasonSchedulingController.js +++ b/src/controllers/reasonSchedulingController.js @@ -55,12 +55,13 @@ const postReason = async (req, res) => { }); } - // new changes + // conditions added to check if timeOffFrom and timeOffTill fields existed if ( foundUser.hasOwnProperty("timeOffFrom") && foundUser.hasOwnProperty("timeOffTill") ) { + // if currentDate is greater than or equal to the last timeOffTill date then both the fields will be updated if (currentDate >= foundUser.timeOffTill) { await UserModel.findOneAndUpdate( { @@ -74,6 +75,7 @@ const postReason = async (req, res) => { } ); } else { + // else only timeOffTill will be updated await UserModel.findOneAndUpdate( { _id: userId, @@ -86,6 +88,7 @@ const postReason = async (req, res) => { ); } } else { + // if both the fields are not present then these fields will be added to mongoDB for that user await UserModel.findOneAndUpdate( { _id: userId, From 35c3a61c0f82bb8403a8d705d561a5239173de98 Mon Sep 17 00:00:00 2001 From: abdelmounaim lallouache Date: Sat, 30 Dec 2023 19:44:01 -0600 Subject: [PATCH 17/36] update --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index ff66994e8..72a8c1013 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5552,7 +5552,7 @@ "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" }, "lodash.merge": { "version": "4.6.2", From 82b95b20977e600573c8c37c2261285730552b83 Mon Sep 17 00:00:00 2001 From: abdelmounaim lallouache Date: Sun, 7 Jan 2024 01:12:38 -0600 Subject: [PATCH 18/36] added endpoint for map country count --- .../profileInitialSetupController.js | 304 ++++++++++-------- src/routes/profileInitialSetupRouter.js | 1 + src/startup/middleware.js | 2 +- 3 files changed, 172 insertions(+), 135 deletions(-) diff --git a/src/controllers/profileInitialSetupController.js b/src/controllers/profileInitialSetupController.js index 12776442e..d89f08064 100644 --- a/src/controllers/profileInitialSetupController.js +++ b/src/controllers/profileInitialSetupController.js @@ -1,10 +1,10 @@ -const mongoose = require('mongoose'); -const { v4: uuidv4 } = require('uuid'); -const moment = require('moment-timezone'); -const jwt = require('jsonwebtoken'); -const emailSender = require('../utilities/emailSender'); -const config = require('../config'); -const cache = require('../utilities/nodeCache')(); +const mongoose = require("mongoose"); +const { v4: uuidv4 } = require("uuid"); +const moment = require("moment-timezone"); +const jwt = require("jsonwebtoken"); +const emailSender = require("../utilities/emailSender"); +const config = require("../config"); +const cache = require("../utilities/nodeCache")(); // returns the email body that includes the setup link for the recipient. function sendLinkMessage(Link) { @@ -78,18 +78,10 @@ function informManagerMessage(user) { const sendEmailWithAcknowledgment = (email, subject, message) => { return new Promise((resolve, reject) => { - emailSender( - email, - subject, - message, - null, - null, - null, - (error,result) => { - if (result) resolve(result) - if (error) reject(result) - } - ); + emailSender(email, subject, message, null, null, null, (error, result) => { + if (result) resolve(result); + if (error) reject(result); + }); }); }; @@ -102,16 +94,18 @@ const profileInitialSetupController = function ( const { JWT_SECRET } = config; const setMapLocation = async (locationData) => { - const location = new MapLocation(locationData); try { - const response = await location.save() - return response + const response = await location.save(); + return response; } catch (err) { - return {type: "Error", message: err.message || 'An error occurred while saving the location'} + return { + type: "Error", + message: err.message || "An error occurred while saving the location", + }; } - } + }; /* Function to handle token generation and email process: @@ -124,13 +118,13 @@ const profileInitialSetupController = function ( let { email, baseUrl, weeklyCommittedHours } = req.body; email = email.toLowerCase(); const token = uuidv4(); - const expiration = moment().tz('America/Los_Angeles').add(1, 'week'); + const expiration = moment().tz("America/Los_Angeles").add(1, "week"); try { const existingEmail = await userProfile.findOne({ email, }); if (existingEmail) { - res.status(400).send('email already in use'); + res.status(400).send("email already in use"); } else { await ProfileInitialSetupToken.findOneAndDelete({ email }); @@ -149,7 +143,7 @@ const profileInitialSetupController = function ( "NEEDED: Complete your One Community profile setup", sendLinkMessage(link) ); - + res.status(200).send(acknowledgment); } } catch (error) { @@ -164,7 +158,7 @@ const profileInitialSetupController = function ( */ const validateSetupToken = async (req, res) => { const { token } = req.body; - const currentMoment = moment.tz('America/Los_Angeles'); + const currentMoment = moment.tz("America/Los_Angeles"); try { const foundToken = await ProfileInitialSetupToken.findOne({ token }); @@ -174,10 +168,10 @@ const profileInitialSetupController = function ( if (expirationMoment.isAfter(currentMoment)) { res.status(200).send(foundToken); } else { - res.status(400).send('Invalid token'); + res.status(400).send("Invalid token"); } } else { - res.status(404).send('Token not found'); + res.status(404).send("Token not found"); } } catch (error) { res.status(500).send(`Error finding token: ${error}`); @@ -195,119 +189,120 @@ const profileInitialSetupController = function ( */ const setUpNewUser = async (req, res) => { const { token } = req.body; - const currentMoment = moment.tz('America/Los_Angeles'); + const currentMoment = moment.tz("America/Los_Angeles"); try { const foundToken = await ProfileInitialSetupToken.findOne({ token }); const existingEmail = await userProfile.findOne({ email: foundToken.email, }); if (existingEmail) { - res.status(400).send('email already in use'); + res.status(400).send("email already in use"); } else if (foundToken) { - const expirationMoment = moment(foundToken.expiration); - - if (expirationMoment.isAfter(currentMoment)) { - const defaultProject = await Project.findOne({ - projectName: 'Orientation and Initial Setup', - }); - - const newUser = new userProfile(); - newUser.password = req.body.password; - newUser.role = 'Volunteer'; - newUser.firstName = req.body.firstName; - newUser.lastName = req.body.lastName; - newUser.jobTitle = req.body.jobTitle; - newUser.phoneNumber = req.body.phoneNumber; - newUser.bio = ''; - newUser.weeklycommittedHours = foundToken.weeklyCommittedHours; - newUser.weeklycommittedHoursHistory = [ - { - hours: newUser.weeklycommittedHours, - dateChanged: Date.now(), - }, - ]; - newUser.personalLinks = []; - newUser.adminLinks = []; - newUser.teams = Array.from(new Set([])); - newUser.projects = Array.from(new Set([defaultProject])); - newUser.createdDate = Date.now(); - newUser.email = req.body.email; - newUser.weeklySummaries = [{ summary: '' }]; - newUser.weeklySummariesCount = 0; - newUser.weeklySummaryOption = 'Required'; - newUser.mediaUrl = ''; - newUser.collaborationPreference = req.body.collaborationPreference; - newUser.timeZone = req.body.timeZone || 'America/Los_Angeles'; - newUser.location = req.body.location; - newUser.profilePic = req.body.profilePicture; - newUser.permissions = { - frontPermissions: [], - backPermissions: [], - }; - newUser.bioPosted = 'default'; - newUser.privacySettings.email = req.body.privacySettings.email; - newUser.privacySettings.phoneNumber = req.body.privacySettings.phoneNumber; - newUser.teamCode = ''; - newUser.isFirstTimelog = true; - - const savedUser = await newUser.save(); - - emailSender( - process.env.MANAGER_EMAIL || 'jae@onecommunityglobal.org', // "jae@onecommunityglobal.org" - `NEW USER REGISTERED: ${savedUser.firstName} ${savedUser.lastName}`, - informManagerMessage(savedUser), - null, - null, - ); - await ProfileInitialSetupToken.findByIdAndDelete(foundToken._id); - - const jwtPayload = { - userid: savedUser._id, - role: savedUser.role, - permissions: savedUser.permissions, - expiryTimestamp: moment().add( - config.TOKEN.Lifetime, - config.TOKEN.Units, - ), - }; - - const token = jwt.sign(jwtPayload, JWT_SECRET); - - const locationData = { - firstName: req.body.firstName, - lastName: req.body.lastName, - jobTitle: req.body.jobTitle, - location: req.body.homeCountry, - } - - res.send({ token }).status(200); - - const mapEntryResult = await setMapLocation(locationData) - if(mapEntryResult.type === "Error"){ - console.log(mapEntryResult.message) - } - - const NewUserCache = { - permissions: savedUser.permissions, - isActive: true, - weeklycommittedHours: savedUser.weeklycommittedHours, - createdDate: savedUser.createdDate.toISOString(), - _id: savedUser._id, - role: savedUser.role, - firstName: savedUser.firstName, - lastName: savedUser.lastName, - email: savedUser.email, - }; - - const allUserCache = JSON.parse(cache.getCache('allusers')); - allUserCache.push(NewUserCache); - cache.setCache('allusers', JSON.stringify(allUserCache)); - } else { - res.status(400).send('Token is expired'); + const expirationMoment = moment(foundToken.expiration); + + if (expirationMoment.isAfter(currentMoment)) { + const defaultProject = await Project.findOne({ + projectName: "Orientation and Initial Setup", + }); + + const newUser = new userProfile(); + newUser.password = req.body.password; + newUser.role = "Volunteer"; + newUser.firstName = req.body.firstName; + newUser.lastName = req.body.lastName; + newUser.jobTitle = req.body.jobTitle; + newUser.phoneNumber = req.body.phoneNumber; + newUser.bio = ""; + newUser.weeklycommittedHours = foundToken.weeklyCommittedHours; + newUser.weeklycommittedHoursHistory = [ + { + hours: newUser.weeklycommittedHours, + dateChanged: Date.now(), + }, + ]; + newUser.personalLinks = []; + newUser.adminLinks = []; + newUser.teams = Array.from(new Set([])); + newUser.projects = Array.from(new Set([defaultProject])); + newUser.createdDate = Date.now(); + newUser.email = req.body.email; + newUser.weeklySummaries = [{ summary: "" }]; + newUser.weeklySummariesCount = 0; + newUser.weeklySummaryOption = "Required"; + newUser.mediaUrl = ""; + newUser.collaborationPreference = req.body.collaborationPreference; + newUser.timeZone = req.body.timeZone || "America/Los_Angeles"; + newUser.location = req.body.location; + newUser.profilePic = req.body.profilePicture; + newUser.permissions = { + frontPermissions: [], + backPermissions: [], + }; + newUser.bioPosted = "default"; + newUser.privacySettings.email = req.body.privacySettings.email; + newUser.privacySettings.phoneNumber = + req.body.privacySettings.phoneNumber; + newUser.teamCode = ""; + newUser.isFirstTimelog = true; + + const savedUser = await newUser.save(); + + emailSender( + process.env.MANAGER_EMAIL || "jae@onecommunityglobal.org", // "jae@onecommunityglobal.org" + `NEW USER REGISTERED: ${savedUser.firstName} ${savedUser.lastName}`, + informManagerMessage(savedUser), + null, + null + ); + await ProfileInitialSetupToken.findByIdAndDelete(foundToken._id); + + const jwtPayload = { + userid: savedUser._id, + role: savedUser.role, + permissions: savedUser.permissions, + expiryTimestamp: moment().add( + config.TOKEN.Lifetime, + config.TOKEN.Units + ), + }; + + const token = jwt.sign(jwtPayload, JWT_SECRET); + + const locationData = { + firstName: req.body.firstName, + lastName: req.body.lastName, + jobTitle: req.body.jobTitle, + location: req.body.homeCountry, + }; + + res.send({ token }).status(200); + + const mapEntryResult = await setMapLocation(locationData); + if (mapEntryResult.type === "Error") { + console.log(mapEntryResult.message); } + + const NewUserCache = { + permissions: savedUser.permissions, + isActive: true, + weeklycommittedHours: savedUser.weeklycommittedHours, + createdDate: savedUser.createdDate.toISOString(), + _id: savedUser._id, + role: savedUser.role, + firstName: savedUser.firstName, + lastName: savedUser.lastName, + email: savedUser.email, + }; + + const allUserCache = JSON.parse(cache.getCache("allusers")); + allUserCache.push(NewUserCache); + cache.setCache("allusers", JSON.stringify(allUserCache)); } else { - res.status(400).send('Invalid token'); + res.status(400).send("Token is expired"); } + } else { + res.status(400).send("Invalid token"); + } } catch (error) { res.status(500).send(`Error: ${error}`); } @@ -331,13 +326,54 @@ const profileInitialSetupController = function ( } }; - + const getTotalCountryCount = async (req, res) => { + try { + const users = []; + const results = await userProfile.find( + {}, + "location totalTangibleHrs hoursByCategory" + ); + + results.forEach((item) => { + if ( + (item.location?.coords.lat && + item.location?.coords.lng && + item.totalTangibleHrs >= 10) || + (item.location?.coords.lat && + item.location?.coords.lng && + calculateTotalHours(item.hoursByCategory) >= 10) + ) { + users.push(item); + } + }); + const modifiedUsers = users.map((item) => ({ + location: item.location, + })); + + const mapUsers = await MapLocation.find({}); + const combined = [...modifiedUsers, ...mapUsers]; + const countries = combined.map((user) => user.location.country); + const totalUniqueCountries = [...new Set(countries)].length; + res.status(200).send({ CountryCount: totalUniqueCountries }); + } catch (error) { + res.status(500).send(`Error: ${error}`); + } + }; + + function calculateTotalHours(hoursByCategory) { + let hours = 0; + Object.keys(hoursByCategory).forEach((x) => { + hours += hoursByCategory[x]; + }); + return hours; + } return { getSetupToken, setUpNewUser, validateSetupToken, getTimeZoneAPIKeyByToken, + getTotalCountryCount, }; }; diff --git a/src/routes/profileInitialSetupRouter.js b/src/routes/profileInitialSetupRouter.js index 12f677224..b514dbe53 100644 --- a/src/routes/profileInitialSetupRouter.js +++ b/src/routes/profileInitialSetupRouter.js @@ -8,6 +8,7 @@ const routes = function (ProfileInitialSetupToken, userProfile, Project , mapLoc ProfileInitialSetup.route('/ProfileInitialSetup').post(controller.setUpNewUser); ProfileInitialSetup.route('/validateToken').post(controller.validateSetupToken); ProfileInitialSetup.route('/getTimeZoneAPIKeyByToken').post(controller.getTimeZoneAPIKeyByToken); + ProfileInitialSetup.route('/getTotalCountryCount').get(controller.getTotalCountryCount); return ProfileInitialSetup; }; diff --git a/src/startup/middleware.js b/src/startup/middleware.js index 23ce1d4a6..5f79f77b8 100644 --- a/src/startup/middleware.js +++ b/src/startup/middleware.js @@ -22,7 +22,7 @@ module.exports = function (app) { next(); return; } - if (req.originalUrl === '/api/ProfileInitialSetup' || req.originalUrl === '/api/validateToken' || req.originalUrl === '/api/getTimeZoneAPIKeyByToken' && req.method === 'POST' + if (req.originalUrl === '/api/ProfileInitialSetup' || req.originalUrl === '/api/validateToken' || req.originalUrl === '/api/getTimeZoneAPIKeyByToken' && req.method === 'POST' || req.originalUrl === '/api/getTotalCountryCount' && req.method === 'GET' ) { next(); return; From f08d2d5066115bfd10ef6d3d5781d7e464438099 Mon Sep 17 00:00:00 2001 From: Tim Kent Date: Sun, 7 Jan 2024 12:18:39 -0800 Subject: [PATCH 19/36] update mats route, controller to use updated model. isolate populate method causing fetch err. --- src/controllers/bmdashboard/bmMaterialsController.js | 10 +++++----- src/routes/bmdashboard/bmMaterialsRouter.js | 4 ++-- src/startup/routes.js | 7 ++++--- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/controllers/bmdashboard/bmMaterialsController.js b/src/controllers/bmdashboard/bmMaterialsController.js index da45aad1d..0191b56fc 100644 --- a/src/controllers/bmdashboard/bmMaterialsController.js +++ b/src/controllers/bmdashboard/bmMaterialsController.js @@ -1,6 +1,6 @@ const mongoose = require('mongoose'); -const bmMaterialsController = function (ItemMaterial, BuildingMaterial) { +const bmMaterialsController = function (BuildingMaterial) { const bmMaterialsList = async function _matsList(req, res) { try { BuildingMaterial.find() @@ -9,10 +9,10 @@ const bmMaterialsController = function (ItemMaterial, BuildingMaterial) { path: 'project', select: '_id name', }, - { - path: 'itemType', - select: '_id name unit', - }, + // { + // path: 'itemType', + // select: '_id name unit', + // }, { path: 'updateRecord', populate: { diff --git a/src/routes/bmdashboard/bmMaterialsRouter.js b/src/routes/bmdashboard/bmMaterialsRouter.js index 9a520d7d6..733148d14 100644 --- a/src/routes/bmdashboard/bmMaterialsRouter.js +++ b/src/routes/bmdashboard/bmMaterialsRouter.js @@ -1,8 +1,8 @@ const express = require('express'); -const routes = function (itemMaterial, buildingMaterial) { +const routes = function (buildingMaterial) { const materialsRouter = express.Router(); - const controller = require('../../controllers/bmdashboard/bmMaterialsController')(itemMaterial, buildingMaterial); + const controller = require('../../controllers/bmdashboard/bmMaterialsController')(buildingMaterial); materialsRouter.route('/materials') .get(controller.bmMaterialsList) .post(controller.bmPurchaseMaterials); diff --git a/src/startup/routes.js b/src/startup/routes.js index 1ac1d669b..492e455cd 100644 --- a/src/startup/routes.js +++ b/src/startup/routes.js @@ -21,10 +21,10 @@ const weeklySummaryAIPrompt = require('../models/weeklySummaryAIPrompt'); const profileInitialSetuptoken = require('../models/profileInitialSetupToken'); const reason = require('../models/reason'); const mouseoverText = require('../models/mouseoverText'); -const inventoryItemMaterial = require('../models/inventoryItemMaterial'); +// const inventoryItemMaterial = require('../models/inventoryItemMaterial'); const mapLocations = require('../models/mapLocation'); const buildingProject = require('../models/bmdashboard/buildingProject'); -const buildingMaterial = require('../models/bmdashboard/buildingMaterial'); +// const buildingMaterial = require('../models/bmdashboard/buildingMaterial'); const { invTypeBase, materialType, @@ -35,6 +35,7 @@ const { } = require('../models/bmdashboard/buildingInventoryType'); const { buildingConsumable, + buildingMaterial, } = require('../models/bmdashboard/buildingInventoryItem'); const buildingTool = require('../models/bmdashboard/buildingTool'); @@ -75,7 +76,7 @@ const mapLocationRouter = require('../routes/mapLocationsRouter')(mapLocations); // bm dashboard const bmLoginRouter = require('../routes/bmdashboard/bmLoginRouter')(); -const bmMaterialsRouter = require('../routes/bmdashboard/bmMaterialsRouter')(inventoryItemMaterial, buildingMaterial); +const bmMaterialsRouter = require('../routes/bmdashboard/bmMaterialsRouter')(buildingMaterial); const bmProjectRouter = require('../routes/bmdashboard/bmProjectRouter')(buildingProject); const bmConsumablesRouter = require('../routes/bmdashboard/bmConsumablesRouter')(buildingConsumable); const bmInventoryTypeRouter = require('../routes/bmdashboard/bmInventoryTypeRouter')(invTypeBase, materialType, consumableType, reusableType, toolType, equipmentType); From 273fa005f4cf6ebd3f1d60b734858cba39fa436b Mon Sep 17 00:00:00 2001 From: Tim Kent Date: Sun, 7 Jan 2024 12:52:01 -0800 Subject: [PATCH 20/36] update populate ref string --- src/controllers/bmdashboard/bmMaterialsController.js | 8 ++++---- src/models/bmdashboard/buildingInventoryItem.js | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/controllers/bmdashboard/bmMaterialsController.js b/src/controllers/bmdashboard/bmMaterialsController.js index 0191b56fc..51295d035 100644 --- a/src/controllers/bmdashboard/bmMaterialsController.js +++ b/src/controllers/bmdashboard/bmMaterialsController.js @@ -9,10 +9,10 @@ const bmMaterialsController = function (BuildingMaterial) { path: 'project', select: '_id name', }, - // { - // path: 'itemType', - // select: '_id name unit', - // }, + { + path: 'itemType', + select: '_id name unit', + }, { path: 'updateRecord', populate: { diff --git a/src/models/bmdashboard/buildingInventoryItem.js b/src/models/bmdashboard/buildingInventoryItem.js index dfaa8c73a..c44f37211 100644 --- a/src/models/bmdashboard/buildingInventoryItem.js +++ b/src/models/bmdashboard/buildingInventoryItem.js @@ -11,7 +11,7 @@ const mongoose = require('mongoose'); // documents stored in 'buildingInventoryItems' collection const smallItemBaseSchema = mongoose.Schema({ - itemType: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingInventoryType' }, + itemType: { type: mongoose.SchemaTypes.ObjectId, ref: 'invTypeBase' }, project: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingProject' }, stockBought: { type: Number, default: 0 }, // total amount of item bought for use in the project // TODO: can stockAvailable default be a function? From 66d3814f11d40fc94c0b028556cde2d1b7e2aaf4 Mon Sep 17 00:00:00 2001 From: Tim Kent Date: Sun, 7 Jan 2024 13:27:58 -0800 Subject: [PATCH 21/36] update brand to brandPref --- .../bmdashboard/bmMaterialsController.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/controllers/bmdashboard/bmMaterialsController.js b/src/controllers/bmdashboard/bmMaterialsController.js index 51295d035..207e2428a 100644 --- a/src/controllers/bmdashboard/bmMaterialsController.js +++ b/src/controllers/bmdashboard/bmMaterialsController.js @@ -42,15 +42,9 @@ const bmMaterialsController = function (BuildingMaterial) { matTypeId, quantity, priority, - brand, + brand: brandPref, requestor: { requestorId }, } = req.body; - const newPurchaseRecord = { - quantity, - priority, - brand, - requestedBy: requestorId, - }; try { // check if requestor has permission to make purchase request //! Note: this code is disabled until permissions are added @@ -64,6 +58,12 @@ const bmMaterialsController = function (BuildingMaterial) { // check if the material is already being used in the project // if no, add a new document to the collection // if yes, update the existing document + const newPurchaseRecord = { + quantity, + priority, + brandPref, + requestedBy: requestorId, + }; const doc = await BuildingMaterial.findOne({ project: projectId, itemType: matTypeId }); if (!doc) { const newDoc = { From 7b4f5431ff0fdf48b097fb9fea7181fef8d93f2d Mon Sep 17 00:00:00 2001 From: Tim Kent Date: Sun, 7 Jan 2024 19:55:18 -0800 Subject: [PATCH 22/36] update lookup func in BuildingProject aggregator to fetch from correct collection and limit results to materials --- src/controllers/bmdashboard/bmProjectController.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/controllers/bmdashboard/bmProjectController.js b/src/controllers/bmdashboard/bmProjectController.js index b6b3e4e18..a4f6712e0 100644 --- a/src/controllers/bmdashboard/bmProjectController.js +++ b/src/controllers/bmdashboard/bmProjectController.js @@ -31,10 +31,11 @@ const bmMProjectController = function (BuildingProject) { { $unwind: '$buildingManager' }, { $lookup: { - from: 'buildingMaterials', + from: 'buildingInventoryItems', let: { id: '$_id' }, pipeline: [ { $match: { $expr: { $eq: ['$project', '$$id'] } } }, + { $match: { __t: 'material_item' } }, { $project: { updateRecord: 0, project: 0 } }, { $lookup: { From 90692149b687e823f3de95c2ba21bf71b03ef376 Mon Sep 17 00:00:00 2001 From: Tim Kent Date: Sun, 7 Jan 2024 20:32:38 -0800 Subject: [PATCH 23/36] update itemType ref in large item schema --- src/models/bmdashboard/buildingInventoryItem.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/bmdashboard/buildingInventoryItem.js b/src/models/bmdashboard/buildingInventoryItem.js index c44f37211..fdcfde3dd 100644 --- a/src/models/bmdashboard/buildingInventoryItem.js +++ b/src/models/bmdashboard/buildingInventoryItem.js @@ -41,7 +41,7 @@ const smallItemBase = mongoose.model('smallItemBase', smallItemBaseSchema, 'buil // documents stored in 'buildingInventoryItems' collection const largeItemBaseSchema = mongoose.Schema({ - itemType: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingInventoryType' }, + itemType: { type: mongoose.SchemaTypes.ObjectId, ref: 'invTypeBase' }, project: { type: mongoose.SchemaTypes.ObjectId, ref: 'buildingProject' }, purchaseStatus: { type: String, enum: ['Rental', 'Purchase'], required: true }, // rental fields are required if purchaseStatus = "Rental" (hopefully correct syntax) From 37ba941687f2a59a3b5741089fd5b7215288590f Mon Sep 17 00:00:00 2001 From: Shiwani Rajagopalan Date: Mon, 8 Jan 2024 03:45:22 -0500 Subject: [PATCH 24/36] added timeOffFrom and timeOffTill fields to new aggregation in taskHelper and dashboardHelper --- src/helpers/dashboardhelper.js | 719 +++++++++++++++++++----------- src/helpers/taskHelper.js | 789 ++++++++++++++++++++------------- 2 files changed, 935 insertions(+), 573 deletions(-) diff --git a/src/helpers/dashboardhelper.js b/src/helpers/dashboardhelper.js index 48f9047da..4a6605213 100644 --- a/src/helpers/dashboardhelper.js +++ b/src/helpers/dashboardhelper.js @@ -3,6 +3,7 @@ const mongoose = require("mongoose"); const userProfile = require("../models/userProfile"); const timeentry = require("../models/timeentry"); const myTeam = require("../helpers/helperModels/myTeam"); +const team = require("../models/team"); const dashboardhelper = function () { const personaldetails = function (userId) { @@ -69,6 +70,16 @@ const dashboardhelper = function () { { $lte: ["$$timeentry.dateOfWork", pdtend], }, + { + $not: [ + { + $in: [ + "$$timeentry.entryType", + ["person", "team", "project"], + ], + }, + ], + }, ], }, }, @@ -152,8 +163,15 @@ const dashboardhelper = function () { return output; }; - const getLeaderboard = function (userId) { + const getLeaderboard = async function (userId) { const userid = mongoose.Types.ObjectId(userId); + const userById = await userProfile + .findOne({ _id: userid, isActive: true }, { role: 1 }) + .then((res) => res) + .catch((e) => {}); + + if (userById == null) return null; + const userRole = userById.role; const pdtstart = moment() .tz("America/Los_Angeles") .startOf("week") @@ -162,274 +180,416 @@ const dashboardhelper = function () { .tz("America/Los_Angeles") .endOf("week") .format("YYYY-MM-DD"); - return myTeam.aggregate([ - { - $match: { - _id: userid, - }, - }, - { - $unwind: "$myteam", - }, - { - $project: { - _id: 0, - role: 1, - personId: "$myteam._id", - name: "$myteam.fullName", - }, - }, - { - $lookup: { - from: "userProfiles", - localField: "personId", - foreignField: "_id", - as: "persondata", - }, - }, - { - $match: { - // leaderboard user roles hierarchy - $or: [ - { - role: { $in: ["Owner", "Core Team"] }, - }, - { - $and: [ - { - role: "Administrator", - }, - { "persondata.0.role": { $nin: ["Owner", "Administrator"] } }, - ], - }, - { - $and: [ - { - role: { $in: ["Manager", "Mentor"] }, - }, - { - "persondata.0.role": { - $nin: [ - "Manager", - "Mentor", - "Core Team", - "Administrator", - "Owner", - ], - }, - }, - ], - }, - { "persondata.0._id": userId }, - { "persondata.0.role": "Volunteer" }, - { "persondata.0.isVisible": true }, - ], - }, - }, - { - $project: { - personId: 1, - name: 1, - role: { - $arrayElemAt: ["$persondata.role", 0], - }, - isVisible: { - $arrayElemAt: ["$persondata.isVisible", 0], - }, - hasSummary: { - $ne: [ - { - $arrayElemAt: [ - { - $arrayElemAt: ["$persondata.weeklySummaries.summary", 0], - }, - 0, - ], - }, - "", - ], - }, - weeklycommittedHours: { - $sum: [ - { - $arrayElemAt: ["$persondata.weeklycommittedHours", 0], - }, - { - $ifNull: [{ $arrayElemAt: ["$persondata.missedHours", 0] }, 0], - }, - ], - }, - timeOffFrom: { - $ifNull: [{ $arrayElemAt: ["$persondata.timeOffFrom", 0] }, null], - }, - timeOffTill: { - $ifNull: [{ $arrayElemAt: ["$persondata.timeOffTill", 0] }, null], - }, - }, - }, - { - $lookup: { - from: "timeEntries", - localField: "personId", - foreignField: "personId", - as: "timeEntryData", - }, - }, - { - $project: { - personId: 1, - name: 1, - role: 1, - isVisible: 1, - hasSummary: 1, - weeklycommittedHours: 1, - timeOffFrom: 1, - timeOffTill: 1, - timeEntryData: { - $filter: { - input: "$timeEntryData", - as: "timeentry", - cond: { - $and: [ - { - $gte: ["$$timeentry.dateOfWork", pdtstart], - }, - { - $lte: ["$$timeentry.dateOfWork", pdtend], - }, - ], - }, - }, - }, - }, - }, - { - $unwind: { - path: "$timeEntryData", - preserveNullAndEmptyArrays: true, - }, - }, - { - $project: { - personId: 1, - name: 1, - role: 1, - isVisible: 1, - hasSummary: 1, - weeklycommittedHours: 1, - timeOffFrom: 1, - timeOffTill: 1, - totalSeconds: { - $cond: [ - { - $gte: ["$timeEntryData.totalSeconds", 0], - }, - "$timeEntryData.totalSeconds", - 0, - ], - }, - isTangible: { - $cond: [ - { - $gte: ["$timeEntryData.totalSeconds", 0], - }, - "$timeEntryData.isTangible", - false, - ], - }, - }, - }, - { - $addFields: { - tangibletime: { - $cond: [ - { - $eq: ["$isTangible", true], - }, - "$totalSeconds", - 0, - ], - }, - intangibletime: { - $cond: [ - { - $eq: ["$isTangible", false], - }, - "$totalSeconds", - 0, - ], - }, - }, - }, - { - $group: { - _id: { - personId: "$personId", - weeklycommittedHours: "$weeklycommittedHours", - timeOffFrom: "$timeOffFrom", - timeOffTill: "$timeOffTill", - name: "$name", - role: "$role", - isVisible: "$isVisible", - hasSummary: "$hasSummary", - }, - totalSeconds: { - $sum: "$totalSeconds", - }, - tangibletime: { - $sum: "$tangibletime", - }, - intangibletime: { - $sum: "$intangibletime", - }, - }, - }, - { - $project: { - _id: 0, - personId: "$_id.personId", - name: "$_id.name", - role: "$_id.role", - isVisible: "$_id.isVisible", - hasSummary: "$_id.hasSummary", - weeklycommittedHours: "$_id.weeklycommittedHours", - totaltime_hrs: { - $divide: ["$totalSeconds", 3600], - }, - totaltangibletime_hrs: { - $divide: ["$tangibletime", 3600], - }, - totalintangibletime_hrs: { - $divide: ["$intangibletime", 3600], - }, - percentagespentintangible: { - $cond: [ - { - $eq: ["$totalSeconds", 0], - }, - 0, - { - $multiply: [ - { - $divide: ["$tangibletime", "$totalSeconds"], - }, - 100, - ], - }, - ], - }, - timeOffFrom: "$_id.timeOffFrom", - timeOffTill: "$_id.timeOffTill", - }, - }, - { - $sort: { - totaltangibletime_hrs: -1, - name: 1, - role: 1, - }, + + let teamMemberIds = [userid]; + let teamMembers = []; + + if ( + userRole != "Administrator" && + userRole != "Owner" && + userRole != "Core Team" + ) { + // Manager , Mentor , Volunteer ... , Show only team members + const teamsResult = await team + .find({ "members.userId": { $in: [userid] } }, { members: 1 }) + .then((res) => res) + .catch((e) => {}); + + teamsResult.map((_myTeam) => { + _myTeam.members.map((teamMember) => { + if (!teamMember.userId.equals(userid)) + teamMemberIds.push(teamMember.userId); + }); + }); + + teamMembers = await userProfile + .find( + { _id: { $in: teamMemberIds }, isActive: true }, + { + role: 1, + firstName: 1, + lastName: 1, + isVisible: 1, + weeklycommittedHours: 1, + weeklySummaries: 1, + timeOffFrom: 1, + timeOffTill: 1, + } + ) + .then((res) => res) + .catch((e) => {}); + } else if (userRole == "Administrator") { + // All users except Owner and Core Team + const excludedRoles = ["Core Team", "Owner"]; + teamMembers = await userProfile + .find( + { isActive: true, role: { $nin: excludedRoles } }, + { + role: 1, + firstName: 1, + lastName: 1, + isVisible: 1, + weeklycommittedHours: 1, + weeklySummaries: 1, + timeOffFrom: 1, + timeOffTill: 1, + } + ) + .then((res) => res) + .catch((e) => {}); + } else { + // 'Core Team', 'Owner' //All users + teamMembers = await userProfile + .find( + { isActive: true }, + { + role: 1, + firstName: 1, + lastName: 1, + isVisible: 1, + weeklycommittedHours: 1, + weeklySummaries: 1, + timeOffFrom: 1, + timeOffTill: 1, + } + ) + .then((res) => res) + .catch((e) => {}); + } + + teamMemberIds = teamMembers.map((member) => member._id); + + const timeEntries = await timeentry.find({ + dateOfWork: { + $gte: pdtstart, + $lte: pdtend, }, - ]); + personId: { $in: teamMemberIds }, + }); + + const timeEntryByPerson = {}; + timeEntries.map((timeEntry) => { + const personIdStr = timeEntry.personId.toString(); + + if (timeEntryByPerson[personIdStr] == null) { + timeEntryByPerson[personIdStr] = { + tangibleSeconds: 0, + intangibleSeconds: 0, + totalSeconds: 0, + }; + } + + if (timeEntry.isTangible === true) { + timeEntryByPerson[personIdStr].tangibleSeconds += + timeEntry.totalSeconds; + } else { + timeEntryByPerson[personIdStr].intangibleSeconds += + timeEntry.totalSeconds; + } + + timeEntryByPerson[personIdStr].totalSeconds += timeEntry.totalSeconds; + }); + + const leaderBoardData = []; + teamMembers.map((teamMember) => { + const obj = { + personId: teamMember._id, + role: teamMember.role, + name: `${teamMember.firstName} ${teamMember.lastName}`, + isVisible: teamMember.isVisible, + hasSummary: + teamMember.weeklySummaries?.length > 0 + ? teamMember.weeklySummaries[0].summary != "" + : false, + weeklycommittedHours: teamMember.weeklycommittedHours, + totaltangibletime_hrs: + timeEntryByPerson[teamMember._id.toString()]?.tangibleSeconds / + 3600 || 0, + totalintangibletime_hrs: + timeEntryByPerson[teamMember._id.toString()]?.intangibleSeconds / + 3600 || 0, + totaltime_hrs: + timeEntryByPerson[teamMember._id.toString()]?.totalSeconds / 3600 || + 0, + percentagespentintangible: + timeEntryByPerson[teamMember._id.toString()] && + timeEntryByPerson[teamMember._id.toString()]?.totalSeconds != 0 && + timeEntryByPerson[teamMember._id.toString()]?.tangibleSeconds != 0 + ? (timeEntryByPerson[teamMember._id.toString()]?.tangibleSeconds / + timeEntryByPerson[teamMember._id.toString()]?.totalSeconds) * + 100 + : 0, + timeOffFrom: teamMember.timeOffFrom || null, + timeOffTill: teamMember.timeOffTill || null, + }; + leaderBoardData.push(obj); + }); + + const sortedLBData = leaderBoardData.sort((a, b) => { + // Sort by totaltangibletime_hrs in descending order + if (b.totaltangibletime_hrs !== a.totaltangibletime_hrs) { + return b.totaltangibletime_hrs - a.totaltangibletime_hrs; + } + + // Then sort by name in ascending order + if (a.name !== b.name) { + return a.name.localeCompare(b.name); + } + + // Finally, sort by role in ascending order + return a.role.localeCompare(b.role); + }); + + return sortedLBData; + + // return myTeam.aggregate([ + // { + // $match: { + // _id: userid, + // }, + // }, + // { + // $unwind: '$myteam', + // }, + // { + // $project: { + // _id: 0, + // role: 1, + // personId: '$myteam._id', + // name: '$myteam.fullName', + // }, + // }, + // { + // $lookup: { + // from: 'userProfiles', + // localField: 'personId', + // foreignField: '_id', + // as: 'persondata', + // }, + // }, + // { + // $match: { + // // leaderboard user roles hierarchy + // $or: [ + // { + // role: { $in: ['Owner', 'Core Team'] }, + // }, + // { + // $and: [ + // { + // role: 'Administrator', + // }, + // { 'persondata.0.role': { $nin: ['Owner', 'Administrator'] } }, + // ], + // }, + // { + // $and: [ + // { + // role: { $in: ['Manager', 'Mentor'] }, + // }, + // { + // 'persondata.0.role': { + // $nin: ['Manager', 'Mentor', 'Core Team', 'Administrator', 'Owner'], + // }, + // }, + // ], + // }, + // { 'persondata.0._id': userId }, + // { 'persondata.0.role': 'Volunteer' }, + // { 'persondata.0.isVisible': true }, + // ], + // }, + // }, + // { + // $project: { + // personId: 1, + // name: 1, + // role: { + // $arrayElemAt: ['$persondata.role', 0], + // }, + // isVisible: { + // $arrayElemAt: ['$persondata.isVisible', 0], + // }, + // hasSummary: { + // $ne: [ + // { + // $arrayElemAt: [ + // { + // $arrayElemAt: ['$persondata.weeklySummaries.summary', 0], + // }, + // 0, + // ], + // }, + // '', + // ], + // }, + // weeklycommittedHours: { + // $sum: [ + // { + // $arrayElemAt: ['$persondata.weeklycommittedHours', 0], + // }, + // { + // $ifNull: [{ $arrayElemAt: ['$persondata.missedHours', 0] }, 0], + // }, + // ], + // }, + // }, + // }, + // { + // $lookup: { + // from: 'timeEntries', + // localField: 'personId', + // foreignField: 'personId', + // as: 'timeEntryData', + // }, + // }, + // { + // $project: { + // personId: 1, + // name: 1, + // role: 1, + // isVisible: 1, + // hasSummary: 1, + // weeklycommittedHours: 1, + // timeEntryData: { + // $filter: { + // input: '$timeEntryData', + // as: 'timeentry', + // cond: { + // $and: [ + // { + // $gte: ['$$timeentry.dateOfWork', pdtstart], + // }, + // { + // $lte: ['$$timeentry.dateOfWork', pdtend], + // }, + // ], + // }, + // }, + // }, + // }, + // }, + // { + // $unwind: { + // path: '$timeEntryData', + // preserveNullAndEmptyArrays: true, + // }, + // }, + // { + // $project: { + // personId: 1, + // name: 1, + // role: 1, + // isVisible: 1, + // hasSummary: 1, + // weeklycommittedHours: 1, + // totalSeconds: { + // $cond: [ + // { + // $gte: ['$timeEntryData.totalSeconds', 0], + // }, + // '$timeEntryData.totalSeconds', + // 0, + // ], + // }, + // isTangible: { + // $cond: [ + // { + // $gte: ['$timeEntryData.totalSeconds', 0], + // }, + // '$timeEntryData.isTangible', + // false, + // ], + // }, + // }, + // }, + // { + // $addFields: { + // tangibletime: { + // $cond: [ + // { + // $eq: ['$isTangible', true], + // }, + // '$totalSeconds', + // 0, + // ], + // }, + // intangibletime: { + // $cond: [ + // { + // $eq: ['$isTangible', false], + // }, + // '$totalSeconds', + // 0, + // ], + // }, + // }, + // }, + // { + // $group: { + // _id: { + // personId: '$personId', + // weeklycommittedHours: '$weeklycommittedHours', + // name: '$name', + // role: '$role', + // isVisible: '$isVisible', + // hasSummary: '$hasSummary', + // }, + // totalSeconds: { + // $sum: '$totalSeconds', + // }, + // tangibletime: { + // $sum: '$tangibletime', + // }, + // intangibletime: { + // $sum: '$intangibletime', + // }, + // }, + // }, + // { + // $project: { + // _id: 0, + // personId: '$_id.personId', + // name: '$_id.name', + // role: '$_id.role', + // isVisible: '$_id.isVisible', + // hasSummary: '$_id.hasSummary', + // weeklycommittedHours: '$_id.weeklycommittedHours', + // totaltime_hrs: { + // $divide: ['$totalSeconds', 3600], + // }, + // totaltangibletime_hrs: { + // $divide: ['$tangibletime', 3600], + // }, + // totalintangibletime_hrs: { + // $divide: ['$intangibletime', 3600], + // }, + // percentagespentintangible: { + // $cond: [ + // { + // $eq: ['$totalSeconds', 0], + // }, + // 0, + // { + // $multiply: [ + // { + // $divide: ['$tangibletime', '$totalSeconds'], + // }, + // 100, + // ], + // }, + // ], + // }, + // }, + // }, + // { + // $sort: { + // totaltangibletime_hrs: -1, + // name: 1, + // role: 1, + // }, + // }, + // ]); }; /** @@ -458,6 +618,7 @@ const dashboardhelper = function () { $gte: pdtStart, $lte: pdtEnd, }, + entryType: { $in: ["default", null] }, personId: userId, }); @@ -485,8 +646,6 @@ const dashboardhelper = function () { totalintangibletime_hrs: intangibleSeconds / 3600, percentagespentintangible: (intangibleSeconds / tangibleSeconds) * 100, - timeOffFrom: user.timeOffFrom, - timeOffTill: user.timeOffTill, }, ]; } catch (err) { @@ -597,6 +756,16 @@ const dashboardhelper = function () { { $lte: ["$$timeentry.dateOfWork", todate], }, + { + $not: [ + { + $in: [ + "$$timeentry.entryType", + ["person", "team", "project"], + ], + }, + ], + }, ], }, }, @@ -674,6 +843,16 @@ const dashboardhelper = function () { { $lte: ["$$timeentry.dateOfWork", todate], }, + { + $not: [ + { + $in: [ + "$$timeentry.entryType", + ["person", "team", "project"], + ], + }, + ], + }, ], }, }, diff --git a/src/helpers/taskHelper.js b/src/helpers/taskHelper.js index b5268734e..f96257a67 100644 --- a/src/helpers/taskHelper.js +++ b/src/helpers/taskHelper.js @@ -1,9 +1,37 @@ const moment = require("moment-timezone"); +const mongoose = require("mongoose"); const userProfile = require("../models/userProfile"); -const myteam = require("../helpers/helperModels/myTeam"); +const timeentry = require("../models/timeentry"); +const myTeam = require("../helpers/helperModels/myTeam"); +const team = require("../models/team"); +const Task = require("../models/task"); +const TaskNotification = require("../models/taskNotification"); +const Wbs = require("../models/wbs"); const taskHelper = function () { - const getTasksForTeams = function (userId) { + const getTasksForTeams = async function (userId) { + const userid = mongoose.Types.ObjectId(userId); + const userById = await userProfile + .findOne( + { _id: userid, isActive: true }, + { + role: 1, + firstName: 1, + lastName: 1, + role: 1, + isVisible: 1, + weeklycommittedHours: 1, + weeklySummaries: 1, + timeOffFrom: 1, + timeOffTill: 1, + } + ) + .then((res) => res) + .catch((e) => {}); + + if (userById == null) return null; + const userRole = userById.role; + const pdtstart = moment() .tz("America/Los_Angeles") .startOf("week") @@ -12,308 +40,460 @@ const taskHelper = function () { .tz("America/Los_Angeles") .endOf("week") .format("YYYY-MM-DD"); - return myteam.aggregate([ - { - $match: { - _id: userId, - }, - }, - { - $unwind: "$myteam", - }, - { - $project: { - _id: 0, - personId: "$myteam._id", - name: "$myteam.fullName", - role: 1, - }, - }, - // have personId, name, role - { - $lookup: { - from: "userProfiles", - localField: "personId", - foreignField: "_id", - as: "persondata", - }, - }, - { - $match: { - // dashboard tasks user roles hierarchy - $or: [ - { - role: { $in: ["Owner", "Core Team"] }, - }, - { - $and: [ - { - role: "Administrator", - }, - { "persondata.0.role": { $nin: ["Owner", "Administrator"] } }, - ], - }, - { - $and: [ - { - role: { $in: ["Manager", "Mentor"] }, - }, - { - "persondata.0.role": { - $nin: [ - "Manager", - "Mentor", - "Core Team", - "Administrator", - "Owner", - ], - }, - }, - ], - }, - { "persondata.0._id": userId }, - { "persondata.0.role": "Volunteer" }, - { "persondata.0.isVisible": true }, - ], - }, - }, - { - $project: { - personId: 1, - name: 1, - weeklycommittedHours: { - $sum: [ - { - $arrayElemAt: ["$persondata.weeklycommittedHours", 0], - }, - { - $ifNull: [{ $arrayElemAt: ["$persondata.missedHours", 0] }, 0], - }, - ], - }, - timeOffFrom: { - $ifNull: [{ $arrayElemAt: ["$persondata.timeOffFrom", 0] }, null], - }, - timeOffTill: { - $ifNull: [{ $arrayElemAt: ["$persondata.timeOffTill", 0] }, null], - }, - role: 1, - }, - }, - { - $lookup: { - from: "timeEntries", - localField: "personId", - foreignField: "personId", - as: "timeEntryData", - }, - }, - { - $project: { - personId: 1, - name: 1, - weeklycommittedHours: 1, - timeOffFrom: 1, - timeOffTill: 1, - timeEntryData: { - $filter: { - input: "$timeEntryData", - as: "timeentry", - cond: { - $and: [ - { - $gte: ["$$timeentry.dateOfWork", pdtstart], - }, - { - $lte: ["$$timeentry.dateOfWork", pdtend], - }, - ], - }, - }, - }, - role: 1, - }, - }, - { - $unwind: { - path: "$timeEntryData", - preserveNullAndEmptyArrays: true, - }, - }, - { - $project: { - personId: 1, - name: 1, - weeklycommittedHours: 1, - timeOffFrom: 1, - timeOffTill: 1, - totalSeconds: { - $cond: [ - { - $gte: ["$timeEntryData.totalSeconds", 0], - }, - "$timeEntryData.totalSeconds", - 0, - ], - }, - isTangible: { - $cond: [ - { - $gte: ["$timeEntryData.totalSeconds", 0], - }, - "$timeEntryData.isTangible", - false, - ], - }, - role: 1, - }, - }, - { - $addFields: { - tangibletime: { - $cond: [ - { - $eq: ["$isTangible", true], - }, - "$totalSeconds", - 0, - ], - }, - }, - }, - { - $group: { - _id: { - personId: "$personId", - weeklycommittedHours: "$weeklycommittedHours", - timeOffFrom: "$timeOffFrom", - timeOffTill: "$timeOffTill", - name: "$name", - role: "$role", - }, - totalSeconds: { - $sum: "$totalSeconds", - }, - tangibletime: { - $sum: "$tangibletime", - }, - }, - }, - { - $project: { - _id: 0, - personId: "$_id.personId", - name: "$_id.name", - weeklycommittedHours: "$_id.weeklycommittedHours", - timeOffFrom: "$_id.timeOffFrom", - timeOffTill: "$_id.timeOffTill", - totaltime_hrs: { - $divide: ["$totalSeconds", 3600], - }, - totaltangibletime_hrs: { - $divide: ["$tangibletime", 3600], - }, - role: "$_id.role", - }, - }, - { - $lookup: { - from: "tasks", - localField: "personId", - foreignField: "resources.userID", - as: "tasks", - }, - }, - { - $project: { - tasks: { - resources: { - profilePic: 0, - }, - }, - }, - }, - { - $unwind: { - path: "$tasks", - preserveNullAndEmptyArrays: true, - }, - }, - { - $lookup: { - from: "wbs", - localField: "tasks.wbsId", - foreignField: "_id", - as: "projectId", - }, - }, - { - $addFields: { - "tasks.projectId": { - $cond: [ - { $ne: ["$projectId", []] }, - { $arrayElemAt: ["$projectId", 0] }, - "$tasks.projectId", - ], - }, - }, - }, - { - $project: { - projectId: 0, - tasks: { - projectId: { - _id: 0, - isActive: 0, - modifiedDatetime: 0, - wbsName: 0, - createdDatetime: 0, - __v: 0, - }, - }, - }, - }, - { - $addFields: { - "tasks.projectId": "$tasks.projectId.projectId", - }, - }, - { - $lookup: { - from: "taskNotifications", - localField: "tasks._id", - foreignField: "taskId", - as: "tasks.taskNotifications", - }, - }, - { - $group: { - _id: "$personId", - tasks: { - $push: "$tasks", - }, - data: { - $first: "$$ROOT", - }, - }, - }, - { - $addFields: { - "data.tasks": { - $filter: { - input: "$tasks", - as: "task", - cond: { $ne: ["$$task", {}] }, - }, - }, - }, - }, - { - $replaceRoot: { - newRoot: "$data", - }, + + let teamMemberIds = [userid]; + let teamMembers = []; + + if ( + userRole != "Administrator" && + userRole != "Owner" && + userRole != "Core Team" + ) { + // Manager , Mentor , Volunteer ... , Show only team members + const teamsResult = await team + .find({ "members.userId": { $in: [userid] } }, { members: 1 }) + .then((res) => res) + .catch((e) => {}); + + teamsResult.map((_myTeam) => { + _myTeam.members.map((teamMember) => { + if (!teamMember.userId.equals(userid)) + teamMemberIds.push(teamMember.userId); + }); + }); + + teamMembers = await userProfile + .find( + { _id: { $in: teamMemberIds }, isActive: true }, + { + role: 1, + firstName: 1, + lastName: 1, + weeklycommittedHours: 1, + timeOffFrom: 1, + timeOffTill: 1, + } + ) + .then((res) => res) + .catch((e) => {}); + } else if (userRole == "Administrator") { + // All users except Owner and Core Team + const excludedRoles = ["Core Team", "Owner"]; + teamMembers = await userProfile + .find( + { isActive: true, role: { $nin: excludedRoles } }, + { + role: 1, + firstName: 1, + lastName: 1, + weeklycommittedHours: 1, + timeOffFrom: 1, + timeOffFrom: 1, + } + ) + .then((res) => res) + .catch((e) => {}); + } else { + // 'Core Team', 'Owner' //All users + teamMembers = await userProfile + .find( + { isActive: true }, + { + role: 1, + firstName: 1, + lastName: 1, + weeklycommittedHours: 1, + timeOffFrom: 1, + timeOffTill: 1, + } + ) + .then((res) => res) + .catch((e) => {}); + } + + teamMemberIds = teamMembers.map((member) => member._id); + + const timeEntries = await timeentry.find({ + dateOfWork: { + $gte: pdtstart, + $lte: pdtend, }, - ]); + personId: { $in: teamMemberIds }, + }); + + const timeEntryByPerson = {}; + timeEntries.map((timeEntry) => { + const personIdStr = timeEntry.personId.toString(); + + if (timeEntryByPerson[personIdStr] == null) { + timeEntryByPerson[personIdStr] = { + tangibleSeconds: 0, + intangibleSeconds: 0, + totalSeconds: 0, + }; + } + + if (timeEntry.isTangible === true) { + timeEntryByPerson[personIdStr].tangibleSeconds += + timeEntry.totalSeconds; + } + timeEntryByPerson[personIdStr].totalSeconds += timeEntry.totalSeconds; + }); + + const teamMemberTasks = await Task.find( + { "resources.userID": { $in: teamMemberIds } }, + { "resources.profilePic": 0 } + ).populate({ + path: "wbsId", + select: "projectId", + }); + const teamMemberTaskIds = teamMemberTasks.map((task) => task._id); + const teamMemberTaskNotifications = await TaskNotification.find({ + taskId: { $in: teamMemberTaskIds }, + }); + + const taskNotificationByTaskNdUser = []; + teamMemberTaskNotifications.map((teamMemberTaskNotification) => { + const taskIdStr = teamMemberTaskNotification.taskId.toString(); + const userIdStr = teamMemberTaskNotification.userId.toString(); + const taskNdUserID = `${taskIdStr},${userIdStr}`; + + if (taskNotificationByTaskNdUser[taskNdUserID]) { + taskNotificationByTaskNdUser[taskNdUserID].push( + teamMemberTaskNotification + ); + } else { + taskNotificationByTaskNdUser[taskNdUserID] = [ + teamMemberTaskNotification, + ]; + } + }); + + const taskByPerson = []; + + teamMemberTasks.map((teamMemberTask) => { + const projId = teamMemberTask.wbsId?.projectId; + const _teamMemberTask = { ...teamMemberTask._doc }; + _teamMemberTask.projectId = projId; + const taskIdStr = _teamMemberTask._id.toString(); + + teamMemberTask.resources.map((resource) => { + const resourceIdStr = resource.userID.toString(); + const taskNdUserID = `${taskIdStr},${resourceIdStr}`; + _teamMemberTask.taskNotifications = + taskNotificationByTaskNdUser[taskNdUserID] || []; + if (taskByPerson[resourceIdStr]) { + taskByPerson[resourceIdStr].push(_teamMemberTask); + } else { + taskByPerson[resourceIdStr] = [_teamMemberTask]; + } + }); + }); + + const teamMemberTasksData = []; + teamMembers.map((teamMember) => { + const obj = { + personId: teamMember._id, + role: teamMember.role, + name: `${teamMember.firstName} ${teamMember.lastName}`, + weeklycommittedHours: teamMember.weeklycommittedHours, + timeOffFrom: teamMember.timeOffFrom || null, + timeOffTill: teamMember.timeOffTill || null, + totaltangibletime_hrs: + timeEntryByPerson[teamMember._id.toString()]?.tangibleSeconds / + 3600 || 0, + totaltime_hrs: + timeEntryByPerson[teamMember._id.toString()]?.totalSeconds / 3600 || + 0, + tasks: taskByPerson[teamMember._id.toString()] || [], + }; + teamMemberTasksData.push(obj); + }); + + return teamMemberTasksData; + + // return myteam.aggregate([ + // { + // $match: { + // _id: userId, + // }, + // }, + // { + // $unwind: '$myteam', + // }, + // { + // $project: { + // _id: 0, + // personId: '$myteam._id', + // name: '$myteam.fullName', + // role: 1, + // }, + // }, + // // have personId, name, role + // { + // $lookup: { + // from: 'userProfiles', + // localField: 'personId', + // foreignField: '_id', + // as: 'persondata', + // }, + // }, + // { + // $match: { + // // dashboard tasks user roles hierarchy + // $or: [ + // { + // role: { $in: ['Owner', 'Core Team'] }, + // }, + // { + // $and: [ + // { + // role: 'Administrator', + // }, + // { 'persondata.0.role': { $nin: ['Owner', 'Administrator'] } }, + // ], + // }, + // { + // $and: [ + // { + // role: { $in: ['Manager', 'Mentor'] }, + // }, + // { + // 'persondata.0.role': { + // $nin: ['Manager', 'Mentor', 'Core Team', 'Administrator', 'Owner'], + // }, + // }, + // ], + // }, + // { 'persondata.0._id': userId }, + // { 'persondata.0.role': 'Volunteer' }, + // { 'persondata.0.isVisible': true }, + // ], + // }, + // }, + // { + // $project: { + // personId: 1, + // name: 1, + // weeklycommittedHours: { + // $sum: [ + // { + // $arrayElemAt: ['$persondata.weeklycommittedHours', 0], + // }, + // { + // $ifNull: [{ $arrayElemAt: ['$persondata.missedHours', 0] }, 0], + // }, + // ], + // }, + // role: 1, + // }, + // }, + // { + // $lookup: { + // from: 'timeEntries', + // localField: 'personId', + // foreignField: 'personId', + // as: 'timeEntryData', + // }, + // }, + // { + // $project: { + // personId: 1, + // name: 1, + // weeklycommittedHours: 1, + // timeEntryData: { + // $filter: { + // input: '$timeEntryData', + // as: 'timeentry', + // cond: { + // $and: [ + // { + // $gte: ['$$timeentry.dateOfWork', pdtstart], + // }, + // { + // $lte: ['$$timeentry.dateOfWork', pdtend], + // }, + // ], + // }, + // }, + // }, + // role: 1, + // }, + // }, + // { + // $unwind: { + // path: '$timeEntryData', + // preserveNullAndEmptyArrays: true, + // }, + // }, + // { + // $project: { + // personId: 1, + // name: 1, + // weeklycommittedHours: 1, + // totalSeconds: { + // $cond: [ + // { + // $gte: ['$timeEntryData.totalSeconds', 0], + // }, + // '$timeEntryData.totalSeconds', + // 0, + // ], + // }, + // isTangible: { + // $cond: [ + // { + // $gte: ['$timeEntryData.totalSeconds', 0], + // }, + // '$timeEntryData.isTangible', + // false, + // ], + // }, + // role: 1, + // }, + // }, + // { + // $addFields: { + // tangibletime: { + // $cond: [ + // { + // $eq: ['$isTangible', true], + // }, + // '$totalSeconds', + // 0, + // ], + // }, + // }, + // }, + // { + // $group: { + // _id: { + // personId: '$personId', + // weeklycommittedHours: '$weeklycommittedHours', + // name: '$name', + // role: '$role', + // }, + // totalSeconds: { + // $sum: '$totalSeconds', + // }, + // tangibletime: { + // $sum: '$tangibletime', + // }, + // }, + // }, + // { + // $project: { + // _id: 0, + // personId: '$_id.personId', + // name: '$_id.name', + // weeklycommittedHours: '$_id.weeklycommittedHours', + // totaltime_hrs: { + // $divide: ['$totalSeconds', 3600], + // }, + // totaltangibletime_hrs: { + // $divide: ['$tangibletime', 3600], + // }, + // role: '$_id.role', + // }, + // }, + // { + // $lookup: { + // from: 'tasks', + // localField: 'personId', + // foreignField: 'resources.userID', + // as: 'tasks', + // }, + // }, + // { + // $project: { + // tasks: { + // resources: { + // profilePic: 0, + // }, + // }, + // }, + // }, + // { + // $unwind: { + // path: '$tasks', + // preserveNullAndEmptyArrays: true, + // }, + // }, + // { + // $lookup: { + // from: 'wbs', + // localField: 'tasks.wbsId', + // foreignField: '_id', + // as: 'projectId', + // }, + // }, + // { + // $addFields: { + // 'tasks.projectId': { + // $cond: [ + // { $ne: ['$projectId', []] }, + // { $arrayElemAt: ['$projectId', 0] }, + // '$tasks.projectId', + // ], + // }, + // }, + // }, + // { + // $project: { + // projectId: 0, + // tasks: { + // projectId: { + // _id: 0, + // isActive: 0, + // modifiedDatetime: 0, + // wbsName: 0, + // createdDatetime: 0, + // __v: 0, + // }, + // }, + // }, + // }, + // { + // $addFields: { + // 'tasks.projectId': '$tasks.projectId.projectId', + // }, + // }, + // { + // $lookup: { + // from: 'taskNotifications', + // localField: 'tasks._id', + // foreignField: 'taskId', + // as: 'tasks.taskNotifications', + // }, + // }, + // { + // $group: { + // _id: '$personId', + // tasks: { + // $push: '$tasks', + // }, + // data: { + // $first: '$$ROOT', + // }, + // }, + // }, + // { + // $addFields: { + // 'data.tasks': { + // $filter: { + // input: '$tasks', + // as: 'task', + // cond: { $ne: ['$$task', {}] }, + // }, + // }, + // }, + // }, + // { + // $replaceRoot: { + // newRoot: '$data', + // }, + // }, + // ]); }; const getTasksForSingleUser = function (userId) { const pdtstart = moment() @@ -381,6 +561,9 @@ const taskHelper = function () { { $lte: ["$$timeentry.dateOfWork", pdtend], }, + { + $in: ["$$timeentry.entryType", ["default", null]], + }, ], }, }, From b81b0a15340148a6378ce6aa920e39bfe01a76b2 Mon Sep 17 00:00:00 2001 From: Shiwani Rajagopalan Date: Wed, 6 Dec 2023 17:16:44 -0500 Subject: [PATCH 25/36] added timeOffFrom and timeOffTill fields to mongoDB --- src/controllers/reasonSchedulingController.js | 171 ++++---- src/helpers/dashboardhelper.js | 374 ++++++++++-------- src/models/userProfile.js | 104 +++-- 3 files changed, 387 insertions(+), 262 deletions(-) diff --git a/src/controllers/reasonSchedulingController.js b/src/controllers/reasonSchedulingController.js index 6f872f338..4d56c5a5e 100644 --- a/src/controllers/reasonSchedulingController.js +++ b/src/controllers/reasonSchedulingController.js @@ -1,23 +1,19 @@ - -const moment = require('moment-timezone'); -const UserModel = require('../models/userProfile'); -const ReasonModel = require('../models/reason'); -const emailSender = require('../utilities/emailSender'); - +const moment = require("moment-timezone"); +const UserModel = require("../models/userProfile"); +const ReasonModel = require("../models/reason"); +const emailSender = require("../utilities/emailSender"); const postReason = async (req, res) => { try { const { userId, requestor, reasonData } = req.body; const newDate = moment - .tz(reasonData.date, 'America/Los_Angeles') - .startOf('day'); - const currentDate = moment - .tz('America/Los_Angeles') - .startOf('day'); + .tz(reasonData.date, "America/Los_Angeles") + .startOf("day"); + const currentDate = moment.tz("America/Los_Angeles").startOf("day"); // error case 0 - if (moment.tz(reasonData.date, 'America/Los_Angeles').day() !== 0) { + if (moment.tz(reasonData.date, "America/Los_Angeles").day() !== 0) { return res.status(400).json({ message: "You must choose the Sunday YOU'LL RETURN as your date. This is so your reason ends up as a note on that blue square.", @@ -27,19 +23,19 @@ const postReason = async (req, res) => { if (newDate.isBefore(currentDate)) { return res.status(400).json({ - message: 'You should select a date that is yet to come', + message: "You should select a date that is yet to come", errorCode: 7, }); } if (!reasonData.message) { return res.status(400).json({ - message: 'You must provide a reason.', + message: "You must provide a reason.", errorCode: 6, }); } - // Commented this condition to make reason scheduler available to all the users. + // Commented this condition to make reason scheduler available to all the users. // error case 1 // if (requestor.role !== 'Owner' && requestor.role !== 'Administrator') { // return res.status(403).json({ @@ -54,15 +50,61 @@ const postReason = async (req, res) => { // error case 2 if (!foundUser) { return res.status(404).json({ - message: 'User not found', + message: "User not found", errorCode: 2, }); } + // new changes + + if ( + foundUser.hasOwnProperty("timeOffFrom") && + foundUser.hasOwnProperty("timeOffTill") + ) { + if (currentDate >= foundUser.timeOffTill) { + await UserModel.findOneAndUpdate( + { + _id: userId, + }, + { + $set: { + timeOffFrom: currentDate, + timeOffTill: newDate, + }, + } + ); + } else { + await UserModel.findOneAndUpdate( + { + _id: userId, + }, + { + $set: { + timeOffTill: newDate, + }, + } + ); + } + } else { + await UserModel.findOneAndUpdate( + { + _id: userId, + }, + { + $set: { + timeOffFrom: currentDate, + timeOffTill: newDate, + }, + } + ); + } + + // + const foundReason = await ReasonModel.findOne({ date: moment - .tz(reasonData.date, 'America/Los_Angeles') - .startOf('day') + .tz(reasonData.date, "America/Los_Angeles") + .startOf("day") .toISOString(), userId, }); @@ -70,14 +112,14 @@ const postReason = async (req, res) => { // error case 3 if (foundReason) { return res.status(403).json({ - message: 'The reason must be unique to the date', + message: "The reason must be unique to the date", errorCode: 3, }); } const savingDate = moment - .tz(reasonData.date, 'America/Los_Angeles') - .startOf('day') + .tz(reasonData.date, "America/Los_Angeles") + .startOf("day") .toISOString(); const newReason = new ReasonModel({ @@ -86,14 +128,13 @@ const postReason = async (req, res) => { userId, }); - // await newReason.save(); - const savedData = await newReason.save(); + const savedData = await newReason.save(); if (savedData) { // Upon clicking the "Save" button in the Blue Square Reason Scheduler, an email will be automatically sent to the user and Jae. - const subject = `Blue Square Reason for ${foundUser.firstName} ${foundUser.lastName} has been set`; + const subject = `Blue Square Reason for ${foundUser.firstName} ${foundUser.lastName} has been set`; - const emailBody = `

Hi !

+ const emailBody = `

Hi !

This email is to let you know that ${foundUser.firstName} ${foundUser.lastName} has set their Blue Square Reason.

@@ -103,21 +144,20 @@ const postReason = async (req, res) => {

Thank you,
One Community

`; + // 1 hardcoded email- emailSender('@gmail.com', subject, emailBody, null, null); - // 1 hardcoded email- emailSender('@gmail.com', subject, emailBody, null, null); - - // 2 user email - - emailSender(`${foundUser.email}`, subject, emailBody, null, null); + // 2 user email - + emailSender(`${foundUser.email}`, subject, emailBody, null, null); - // 3 - user email and hardcoded email ( After PR approval hardcode Jae's email) - // emailSender(`${foundUser.email},@gmail.com`, subject, emailBody, null, null); - } + // 3 - user email and hardcoded email ( After PR approval hardcode Jae's email) + // emailSender(`${foundUser.email},@gmail.com`, subject, emailBody, null, null); + } return res.sendStatus(200); } catch (error) { console.log(error); return res.status(400).json({ - errMessage: 'Something went wrong', + errMessage: "Something went wrong", }); } }; @@ -140,7 +180,7 @@ const getAllReasons = async (req, res) => { // error case 2 if (!foundUser) { return res.status(404).json({ - message: 'User not found', + message: "User not found", }); } @@ -154,7 +194,7 @@ const getAllReasons = async (req, res) => { } catch (error) { console.log(error); return res.status(400).json({ - errMessage: 'Something went wrong while fetching the user', + errMessage: "Something went wrong while fetching the user", }); } }; @@ -178,24 +218,24 @@ const getSingleReason = async (req, res) => { // error case 2 if (!foundUser) { return res.status(404).json({ - message: 'User not found', + message: "User not found", errorCode: 2, }); } const foundReason = await ReasonModel.findOne({ date: moment - .tz(queryDate, 'America/Los_Angeles') - .startOf('day') + .tz(queryDate, "America/Los_Angeles") + .startOf("day") .toISOString(), userId, }); if (!foundReason) { return res.status(200).json({ - reason: '', - date: '', - userId: '', + reason: "", + date: "", + userId: "", isSet: false, }); } @@ -204,7 +244,7 @@ const getSingleReason = async (req, res) => { } catch (error) { console.log(error); return res.status(400).json({ - message: 'Something went wrong while fetching single reason', + message: "Something went wrong while fetching single reason", }); } }; @@ -225,7 +265,7 @@ const patchReason = async (req, res) => { if (!reasonData.message) { return res.status(400).json({ - message: 'You must provide a reason.', + message: "You must provide a reason.", errorCode: 6, }); } @@ -235,22 +275,22 @@ const patchReason = async (req, res) => { // error case 2 if (!foundUser) { return res.status(404).json({ - message: 'User not found', + message: "User not found", errorCode: 2, }); } const foundReason = await ReasonModel.findOne({ date: moment - .tz(reasonData.date, 'America/Los_Angeles') - .startOf('day') + .tz(reasonData.date, "America/Los_Angeles") + .startOf("day") .toISOString(), userId, }); // error case 4 if (!foundReason) { return res.status(404).json({ - message: 'Reason not found', + message: "Reason not found", errorCode: 4, }); } @@ -260,9 +300,9 @@ const patchReason = async (req, res) => { const savedData = await foundReason.save(); if (savedData) { // Upon clicking the "Save" button in the Blue Square Reason Scheduler, an email will be automatically sent to the user and Jae. - const subject = `Blue Square Reason for ${foundUser.firstName} ${foundUser.lastName} has been updated`; + const subject = `Blue Square Reason for ${foundUser.firstName} ${foundUser.lastName} has been updated`; - const emailBody = `

Hi !

+ const emailBody = `

Hi !

This email is to let you know that ${foundUser.firstName} ${foundUser.lastName} has updated their Blue Square Reason.

@@ -272,22 +312,21 @@ const patchReason = async (req, res) => {

Thank you,
One Community

`; + // 1 hardcoded email- emailSender('@gmail.com', subject, emailBody, null, null); - // 1 hardcoded email- emailSender('@gmail.com', subject, emailBody, null, null); + // 2 user email - + emailSender(`${foundUser.email}`, subject, emailBody, null, null); - // 2 user email - - emailSender(`${foundUser.email}`, subject, emailBody, null, null); - - // 3 - user email and hardcoded email ( After PR approval hardcode Jae's email) - // emailSender(`${foundUser.email},@gmail.com`, subject, emailBody, null, null); - } + // 3 - user email and hardcoded email ( After PR approval hardcode Jae's email) + // emailSender(`${foundUser.email},@gmail.com`, subject, emailBody, null, null); + } return res.status(200).json({ - message: 'Reason Updated!', + message: "Reason Updated!", }); } catch (error) { return res.status(400).json({ - message: 'something went wrong while patching the reason', + message: "something went wrong while patching the reason", }); } }; @@ -298,10 +337,10 @@ const deleteReason = async (req, res) => { const { userId } = req.params; // error case 1 - if (requestor.role !== 'Owner' && requestor.role !== 'Administrator') { + if (requestor.role !== "Owner" && requestor.role !== "Administrator") { return res.status(403).json({ message: - 'You must be an Owner or Administrator to schedule a reason for a Blue Square', + "You must be an Owner or Administrator to schedule a reason for a Blue Square", errorCode: 1, }); } @@ -311,21 +350,21 @@ const deleteReason = async (req, res) => { // error case 2 if (!foundUser) { return res.status(404).json({ - message: 'User not found', + message: "User not found", errorCode: 2, }); } const foundReason = await ReasonModel.findOne({ date: moment - .tz(reasonData.date, 'America/Los_Angeles') - .startOf('day') + .tz(reasonData.date, "America/Los_Angeles") + .startOf("day") .toISOString(), }); if (!foundReason) { return res.status(404).json({ - message: 'Reason not found', + message: "Reason not found", errorCode: 4, }); } @@ -333,13 +372,13 @@ const deleteReason = async (req, res) => { foundReason.remove((err) => { if (err) { return res.status(500).json({ - message: 'Error while deleting document', + message: "Error while deleting document", errorCode: 5, }); } return res.status(200).json({ - message: 'Document deleted', + message: "Document deleted", }); }); } catch (error) {} diff --git a/src/helpers/dashboardhelper.js b/src/helpers/dashboardhelper.js index 9bedb0e8f..a200a0095 100644 --- a/src/helpers/dashboardhelper.js +++ b/src/helpers/dashboardhelper.js @@ -1,28 +1,27 @@ -const moment = require('moment-timezone'); -const mongoose = require('mongoose'); -const userProfile = require('../models/userProfile'); -const timeentry = require('../models/timeentry'); -const myTeam = require('../helpers/helperModels/myTeam'); -const team = require('../models/team'); - +const moment = require("moment-timezone"); +const mongoose = require("mongoose"); +const userProfile = require("../models/userProfile"); +const timeentry = require("../models/timeentry"); +const myTeam = require("../helpers/helperModels/myTeam"); +const team = require("../models/team"); const dashboardhelper = function () { const personaldetails = function (userId) { return userProfile.findById( userId, - '_id firstName lastName role profilePic badgeCollection', + "_id firstName lastName role profilePic badgeCollection" ); }; const getOrgData = async function () { const pdtstart = moment() - .tz('America/Los_Angeles') - .startOf('week') - .format('YYYY-MM-DD'); + .tz("America/Los_Angeles") + .startOf("week") + .format("YYYY-MM-DD"); const pdtend = moment() - .tz('America/Los_Angeles') - .endOf('week') - .format('YYYY-MM-DD'); + .tz("America/Los_Angeles") + .endOf("week") + .format("YYYY-MM-DD"); /** * Previous aggregate pipeline had two issues: @@ -41,40 +40,43 @@ const dashboardhelper = function () { $gte: 1, }, role: { - $ne: 'Mentor', + $ne: "Mentor", }, }, }, { $lookup: { - from: 'timeEntries', - localField: '_id', - foreignField: 'personId', - as: 'timeEntryData', + from: "timeEntries", + localField: "_id", + foreignField: "personId", + as: "timeEntryData", }, }, { $project: { - personId: '$_id', + personId: "$_id", name: 1, weeklycommittedHours: 1, role: 1, timeEntryData: { $filter: { - input: '$timeEntryData', - as: 'timeentry', + input: "$timeEntryData", + as: "timeentry", cond: { $and: [ { - $gte: ['$$timeentry.dateOfWork', pdtstart], + $gte: ["$$timeentry.dateOfWork", pdtstart], }, { - $lte: ['$$timeentry.dateOfWork', pdtend], + $lte: ["$$timeentry.dateOfWork", pdtend], }, { $not: [ { - $in: ['$$timeentry.entryType', ['person', 'team', 'project']], + $in: [ + "$$timeentry.entryType", + ["person", "team", "project"], + ], }, ], }, @@ -86,7 +88,7 @@ const dashboardhelper = function () { }, { $unwind: { - path: '$timeEntryData', + path: "$timeEntryData", preserveNullAndEmptyArrays: true, }, }, @@ -97,27 +99,27 @@ const dashboardhelper = function () { totalSeconds: { $cond: [ { - $gte: ['$timeEntryData.totalSeconds', 0], + $gte: ["$timeEntryData.totalSeconds", 0], }, - '$timeEntryData.totalSeconds', + "$timeEntryData.totalSeconds", 0, ], }, tangibletime: { $cond: [ { - $eq: ['$timeEntryData.isTangible', true], + $eq: ["$timeEntryData.isTangible", true], }, - '$timeEntryData.totalSeconds', + "$timeEntryData.totalSeconds", 0, ], }, intangibletime: { $cond: [ { - $eq: ['$timeEntryData.isTangible', false], + $eq: ["$timeEntryData.isTangible", false], }, - '$timeEntryData.totalSeconds', + "$timeEntryData.totalSeconds", 0, ], }, @@ -126,17 +128,17 @@ const dashboardhelper = function () { { $group: { _id: { - personId: '$personId', - weeklycommittedHours: '$weeklycommittedHours', + personId: "$personId", + weeklycommittedHours: "$weeklycommittedHours", }, time_hrs: { - $sum: { $divide: ['$totalSeconds', 3600] }, + $sum: { $divide: ["$totalSeconds", 3600] }, }, tangibletime_hrs: { - $sum: { $divide: ['$tangibletime', 3600] }, + $sum: { $divide: ["$tangibletime", 3600] }, }, intangibletime_hrs: { - $sum: { $divide: ['$intangibletime', 3600] }, + $sum: { $divide: ["$intangibletime", 3600] }, }, }, }, @@ -144,15 +146,15 @@ const dashboardhelper = function () { $group: { _id: 0, memberCount: { $sum: 1 }, - totalweeklycommittedHours: { $sum: '$_id.weeklycommittedHours' }, + totalweeklycommittedHours: { $sum: "$_id.weeklycommittedHours" }, totaltime_hrs: { - $sum: '$time_hrs', + $sum: "$time_hrs", }, totaltangibletime_hrs: { - $sum: '$tangibletime_hrs', + $sum: "$tangibletime_hrs", }, totalintangibletime_hrs: { - $sum: '$intangibletime_hrs', + $sum: "$intangibletime_hrs", }, }, }, @@ -163,55 +165,93 @@ const dashboardhelper = function () { const getLeaderboard = async function (userId) { const userid = mongoose.Types.ObjectId(userId); - const userById = await userProfile.findOne({ _id: userid, isActive: true }, { role: 1 }) - .then(res => res).catch((e) => {}); + const userById = await userProfile + .findOne({ _id: userid, isActive: true }, { role: 1 }) + .then((res) => res) + .catch((e) => {}); if (userById == null) return null; const userRole = userById.role; const pdtstart = moment() - .tz('America/Los_Angeles') - .startOf('week') - .format('YYYY-MM-DD'); + .tz("America/Los_Angeles") + .startOf("week") + .format("YYYY-MM-DD"); const pdtend = moment() - .tz('America/Los_Angeles') - .endOf('week') - .format('YYYY-MM-DD'); + .tz("America/Los_Angeles") + .endOf("week") + .format("YYYY-MM-DD"); let teamMemberIds = [userid]; let teamMembers = []; - if (userRole != 'Administrator' && userRole != 'Owner' && userRole != 'Core Team') // Manager , Mentor , Volunteer ... , Show only team members - { - const teamsResult = await team.find({ 'members.userId': { $in: [userid] } }, { members: 1 }) - .then(res => res).catch((e) => {}); + if ( + userRole != "Administrator" && + userRole != "Owner" && + userRole != "Core Team" + ) { + // Manager , Mentor , Volunteer ... , Show only team members + const teamsResult = await team + .find({ "members.userId": { $in: [userid] } }, { members: 1 }) + .then((res) => res) + .catch((e) => {}); teamsResult.map((_myTeam) => { _myTeam.members.map((teamMember) => { - if (!teamMember.userId.equals(userid)) teamMemberIds.push(teamMember.userId); - }); + if (!teamMember.userId.equals(userid)) + teamMemberIds.push(teamMember.userId); + }); }); - teamMembers = await userProfile.find({ _id: { $in: teamMemberIds }, isActive: true }, + teamMembers = await userProfile + .find( + { _id: { $in: teamMemberIds }, isActive: true }, { - role: 1, firstName: 1, lastName: 1, isVisible: 1, weeklycommittedHours: 1, weeklySummaries: 1, -}) - .then(res => res).catch((e) => {}); - } else if (userRole == 'Administrator') { // All users except Owner and Core Team - const excludedRoles = ['Core Team', 'Owner']; - teamMembers = await userProfile.find({ isActive: true, role: { $nin: excludedRoles } }, + role: 1, + firstName: 1, + lastName: 1, + isVisible: 1, + weeklycommittedHours: 1, + weeklySummaries: 1, + } + ) + .then((res) => res) + .catch((e) => {}); + } else if (userRole == "Administrator") { + // All users except Owner and Core Team + const excludedRoles = ["Core Team", "Owner"]; + teamMembers = await userProfile + .find( + { isActive: true, role: { $nin: excludedRoles } }, { - role: 1, firstName: 1, lastName: 1, isVisible: 1, weeklycommittedHours: 1, weeklySummaries: 1, -}) - .then(res => res).catch((e) => {}); - } else { // 'Core Team', 'Owner' //All users - teamMembers = await userProfile.find({ isActive: true }, + role: 1, + firstName: 1, + lastName: 1, + isVisible: 1, + weeklycommittedHours: 1, + weeklySummaries: 1, + } + ) + .then((res) => res) + .catch((e) => {}); + } else { + // 'Core Team', 'Owner' //All users + teamMembers = await userProfile + .find( + { isActive: true }, { - role: 1, firstName: 1, lastName: 1, isVisible: 1, weeklycommittedHours: 1, weeklySummaries: 1, -}) - .then(res => res).catch((e) => {}); - } + role: 1, + firstName: 1, + lastName: 1, + isVisible: 1, + weeklycommittedHours: 1, + weeklySummaries: 1, + } + ) + .then((res) => res) + .catch((e) => {}); + } - teamMemberIds = teamMembers.map(member => member._id); + teamMemberIds = teamMembers.map((member) => member._id); const timeEntries = await timeentry.find({ dateOfWork: { @@ -225,36 +265,56 @@ const dashboardhelper = function () { timeEntries.map((timeEntry) => { const personIdStr = timeEntry.personId.toString(); - if (timeEntryByPerson[personIdStr] == null) { timeEntryByPerson[personIdStr] = { tangibleSeconds: 0, intangibleSeconds: 0, totalSeconds: 0 }; } + if (timeEntryByPerson[personIdStr] == null) { + timeEntryByPerson[personIdStr] = { + tangibleSeconds: 0, + intangibleSeconds: 0, + totalSeconds: 0, + }; + } if (timeEntry.isTangible === true) { - timeEntryByPerson[personIdStr].tangibleSeconds += timeEntry.totalSeconds; + timeEntryByPerson[personIdStr].tangibleSeconds += + timeEntry.totalSeconds; } else { - timeEntryByPerson[personIdStr].intangibleSeconds += timeEntry.totalSeconds; + timeEntryByPerson[personIdStr].intangibleSeconds += + timeEntry.totalSeconds; } timeEntryByPerson[personIdStr].totalSeconds += timeEntry.totalSeconds; }); - const leaderBoardData = []; teamMembers.map((teamMember) => { - const obj = { - personId: teamMember._id, - role: teamMember.role, - name: `${teamMember.firstName } ${ teamMember.lastName}`, - isVisible: teamMember.isVisible, - hasSummary: teamMember.weeklySummaries?.length > 0 ? teamMember.weeklySummaries[0].summary != '' : false, - weeklycommittedHours: teamMember.weeklycommittedHours, - totaltangibletime_hrs: ((timeEntryByPerson[teamMember._id.toString()]?.tangibleSeconds / 3600) || 0), - totalintangibletime_hrs: ((timeEntryByPerson[teamMember._id.toString()]?.intangibleSeconds / 3600) || 0), - totaltime_hrs: ((timeEntryByPerson[teamMember._id.toString()]?.totalSeconds / 3600) || 0), - percentagespentintangible: - (timeEntryByPerson[teamMember._id.toString()] && timeEntryByPerson[teamMember._id.toString()]?.totalSeconds != 0 && timeEntryByPerson[teamMember._id.toString()]?.tangibleSeconds != 0) - ? (timeEntryByPerson[teamMember._id.toString()]?.tangibleSeconds / timeEntryByPerson[teamMember._id.toString()]?.totalSeconds) * 100 - : 0, - }; - leaderBoardData.push(obj); + const obj = { + personId: teamMember._id, + role: teamMember.role, + name: `${teamMember.firstName} ${teamMember.lastName}`, + isVisible: teamMember.isVisible, + hasSummary: + teamMember.weeklySummaries?.length > 0 + ? teamMember.weeklySummaries[0].summary != "" + : false, + weeklycommittedHours: teamMember.weeklycommittedHours, + totaltangibletime_hrs: + timeEntryByPerson[teamMember._id.toString()]?.tangibleSeconds / + 3600 || 0, + totalintangibletime_hrs: + timeEntryByPerson[teamMember._id.toString()]?.intangibleSeconds / + 3600 || 0, + totaltime_hrs: + timeEntryByPerson[teamMember._id.toString()]?.totalSeconds / 3600 || + 0, + percentagespentintangible: + timeEntryByPerson[teamMember._id.toString()] && + timeEntryByPerson[teamMember._id.toString()]?.totalSeconds != 0 && + timeEntryByPerson[teamMember._id.toString()]?.tangibleSeconds != 0 + ? (timeEntryByPerson[teamMember._id.toString()]?.tangibleSeconds / + timeEntryByPerson[teamMember._id.toString()]?.totalSeconds) * + 100 + : 0, + }; + leaderBoardData.push(obj); }); const sortedLBData = leaderBoardData.sort((a, b) => { @@ -532,14 +592,14 @@ const dashboardhelper = function () { const getUserLaborData = async function (userId) { try { const pdtStart = moment() - .tz('America/Los_Angeles') - .startOf('week') - .format('YYYY-MM-DD'); + .tz("America/Los_Angeles") + .startOf("week") + .format("YYYY-MM-DD"); const pdtEnd = moment() - .tz('America/Los_Angeles') - .endOf('week') - .format('YYYY-MM-DD'); + .tz("America/Los_Angeles") + .endOf("week") + .format("YYYY-MM-DD"); const user = await userProfile.findById({ _id: userId, @@ -550,7 +610,7 @@ const dashboardhelper = function () { $gte: pdtStart, $lte: pdtEnd, }, - entryType: { $in: ['default', null] }, + entryType: { $in: ["default", null] }, personId: userId, }); @@ -570,7 +630,7 @@ const dashboardhelper = function () { personId: userId, role: user.role, isVisible: user.isVisible, - hasSummary: user.weeklySummaries[0].summary !== '', + hasSummary: user.weeklySummaries[0].summary !== "", weeklycommittedHours: user.weeklycommittedHours, name: `${user.firstName} ${user.lastName}`, totaltime_hrs: (tangibleSeconds + intangibleSeconds) / 3600, @@ -583,8 +643,8 @@ const dashboardhelper = function () { } catch (err) { return [ { - personId: 'error', - name: 'Error Error', + personId: "error", + name: "Error Error", totaltime_hrs: 0, totaltangibletime_hrs: 0, totalintangibletime_hrs: 0, @@ -595,8 +655,8 @@ const dashboardhelper = function () { }; const laborthismonth = function (userId, startDate, endDate) { - const fromdate = moment(startDate).format('YYYY-MM-DD'); - const todate = moment(endDate).format('YYYY-MM-DD'); + const fromdate = moment(startDate).format("YYYY-MM-DD"); + const todate = moment(endDate).format("YYYY-MM-DD"); return timeentry.aggregate([ { @@ -612,19 +672,19 @@ const dashboardhelper = function () { { $group: { _id: { - projectId: '$projectId', + projectId: "$projectId", }, labor: { - $sum: '$totalSeconds', + $sum: "$totalSeconds", }, }, }, { $lookup: { - from: 'projects', - localField: '_id.projectId', - foreignField: '_id', - as: 'project', + from: "projects", + localField: "_id.projectId", + foreignField: "_id", + as: "project", }, }, { @@ -633,13 +693,13 @@ const dashboardhelper = function () { projectName: { $ifNull: [ { - $arrayElemAt: ['$project.projectName', 0], + $arrayElemAt: ["$project.projectName", 0], }, - 'Undefined', + "Undefined", ], }, timeSpent_hrs: { - $divide: ['$labor', 3600], + $divide: ["$labor", 3600], }, }, }, @@ -647,8 +707,8 @@ const dashboardhelper = function () { }; const laborthisweek = function (userId, startDate, endDate) { - const fromdate = moment(startDate).format('YYYY-MM-DD'); - const todate = moment(endDate).format('YYYY-MM-DD'); + const fromdate = moment(startDate).format("YYYY-MM-DD"); + const todate = moment(endDate).format("YYYY-MM-DD"); return userProfile.aggregate([ { @@ -664,10 +724,10 @@ const dashboardhelper = function () { }, { $lookup: { - from: 'timeEntries', - localField: '_id', - foreignField: 'personId', - as: 'timeEntryData', + from: "timeEntries", + localField: "_id", + foreignField: "personId", + as: "timeEntryData", }, }, { @@ -675,23 +735,26 @@ const dashboardhelper = function () { weeklycommittedHours: 1, timeEntryData: { $filter: { - input: '$timeEntryData', - as: 'timeentry', + input: "$timeEntryData", + as: "timeentry", cond: { $and: [ { - $eq: ['$$timeentry.isTangible', true], + $eq: ["$$timeentry.isTangible", true], }, { - $gte: ['$$timeentry.dateOfWork', fromdate], + $gte: ["$$timeentry.dateOfWork", fromdate], }, { - $lte: ['$$timeentry.dateOfWork', todate], + $lte: ["$$timeentry.dateOfWork", todate], }, { $not: [ { - $in: ['$$timeentry.entryType', ['person', 'team', 'project']], + $in: [ + "$$timeentry.entryType", + ["person", "team", "project"], + ], }, ], }, @@ -703,27 +766,27 @@ const dashboardhelper = function () { }, { $unwind: { - path: '$timeEntryData', + path: "$timeEntryData", preserveNullAndEmptyArrays: true, }, }, { $group: { _id: { - _id: '$_id', - weeklycommittedHours: '$weeklycommittedHours', + _id: "$_id", + weeklycommittedHours: "$weeklycommittedHours", }, effort: { - $sum: '$timeEntryData.totalSeconds', + $sum: "$timeEntryData.totalSeconds", }, }, }, { $project: { _id: 0, - weeklycommittedHours: '$_id.weeklycommittedHours', + weeklycommittedHours: "$_id.weeklycommittedHours", timeSpent_hrs: { - $divide: ['$effort', 3600], + $divide: ["$effort", 3600], }, }, }, @@ -731,8 +794,8 @@ const dashboardhelper = function () { }; const laborThisWeekByCategory = function (userId, startDate, endDate) { - const fromdate = moment(startDate).format('YYYY-MM-DD'); - const todate = moment(endDate).format('YYYY-MM-DD'); + const fromdate = moment(startDate).format("YYYY-MM-DD"); + const todate = moment(endDate).format("YYYY-MM-DD"); return userProfile.aggregate([ { @@ -748,10 +811,10 @@ const dashboardhelper = function () { }, { $lookup: { - from: 'timeEntries', - localField: '_id', - foreignField: 'personId', - as: 'timeEntryData', + from: "timeEntries", + localField: "_id", + foreignField: "personId", + as: "timeEntryData", }, }, { @@ -759,23 +822,26 @@ const dashboardhelper = function () { weeklycommittedHours: 1, timeEntryData: { $filter: { - input: '$timeEntryData', - as: 'timeentry', + input: "$timeEntryData", + as: "timeentry", cond: { $and: [ { - $eq: ['$$timeentry.isTangible', true], + $eq: ["$$timeentry.isTangible", true], }, { - $gte: ['$$timeentry.dateOfWork', fromdate], + $gte: ["$$timeentry.dateOfWork", fromdate], }, { - $lte: ['$$timeentry.dateOfWork', todate], + $lte: ["$$timeentry.dateOfWork", todate], }, { $not: [ { - $in: ['$$timeentry.entryType', ['person', 'team', 'project']], + $in: [ + "$$timeentry.entryType", + ["person", "team", "project"], + ], }, ], }, @@ -787,37 +853,37 @@ const dashboardhelper = function () { }, { $unwind: { - path: '$timeEntryData', + path: "$timeEntryData", preserveNullAndEmptyArrays: true, }, }, { $group: { - _id: '$timeEntryData.projectId', + _id: "$timeEntryData.projectId", effort: { - $sum: '$timeEntryData.totalSeconds', + $sum: "$timeEntryData.totalSeconds", }, }, }, { $lookup: { - from: 'projects', - localField: '_id', - foreignField: '_id', - as: 'project', + from: "projects", + localField: "_id", + foreignField: "_id", + as: "project", }, }, { $unwind: { - path: '$project', + path: "$project", preserveNullAndEmptyArrays: true, }, }, { $group: { - _id: '$project.category', + _id: "$project.category", effort: { - $sum: '$effort', + $sum: "$effort", }, }, }, @@ -825,7 +891,7 @@ const dashboardhelper = function () { $project: { _id: 1, timeSpent_hrs: { - $divide: ['$effort', 3600], + $divide: ["$effort", 3600], }, }, }, diff --git a/src/models/userProfile.js b/src/models/userProfile.js index 8a8de2e18..762b61c02 100644 --- a/src/models/userProfile.js +++ b/src/models/userProfile.js @@ -1,9 +1,9 @@ -const mongoose = require('mongoose'); -const moment = require('moment-timezone'); +const mongoose = require("mongoose"); +const moment = require("moment-timezone"); const { Schema } = mongoose; -const validate = require('mongoose-validator'); -const bcrypt = require('bcryptjs'); +const validate = require("mongoose-validator"); +const bcrypt = require("bcryptjs"); const SALT_ROUNDS = 10; const nextDay = new Date(); @@ -15,11 +15,12 @@ const userProfileSchema = new Schema({ required: true, validate: { validator(v) { - const passwordregex = /(?=^.{8,}$)((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/; + const passwordregex = + /(?=^.{8,}$)((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/; return passwordregex.test(v); }, message: - '{VALUE} is not a valid password!password should be at least 8 charcaters long with uppercase, lowercase and number/special char.', + "{VALUE} is not a valid password!password should be at least 8 charcaters long with uppercase, lowercase and number/special char.", }, }, isActive: { type: Boolean, required: true, default: true }, @@ -47,7 +48,9 @@ const userProfileSchema = new Schema({ type: String, required: true, unique: true, - validate: [validate({ validator: 'isEmail', message: 'Email address is invalid' })], + validate: [ + validate({ validator: "isEmail", message: "Email address is invalid" }), + ], }, weeklycommittedHours: { type: Number, default: 10 }, weeklycommittedHoursHistory: [ @@ -60,13 +63,15 @@ const userProfileSchema = new Schema({ createdDate: { type: Date, required: true, default: nextDay }, lastModifiedDate: { type: Date, required: true, default: Date.now() }, reactivationDate: { type: Date }, - personalLinks: [{ _id: Schema.Types.ObjectId, Name: String, Link: { type: String } }], + personalLinks: [ + { _id: Schema.Types.ObjectId, Name: String, Link: { type: String } }, + ], adminLinks: [{ _id: Schema.Types.ObjectId, Name: String, Link: String }], - teams: [{ type: mongoose.SchemaTypes.ObjectId, ref: 'team' }], - projects: [{ type: mongoose.SchemaTypes.ObjectId, ref: 'project' }], + teams: [{ type: mongoose.SchemaTypes.ObjectId, ref: "team" }], + projects: [{ type: mongoose.SchemaTypes.ObjectId, ref: "project" }], badgeCollection: [ { - badge: { type: mongoose.SchemaTypes.ObjectId, ref: 'badge' }, + badge: { type: mongoose.SchemaTypes.ObjectId, ref: "badge" }, count: { type: Number, default: 0 }, earnedDate: { type: Array, default: [] }, lastModified: { type: Date, required: true, default: Date.now() }, @@ -79,20 +84,25 @@ const userProfileSchema = new Schema({ ], profilePic: { type: String }, infringements: [ - { date: { type: String, required: true }, description: { type: String, required: true } }, + { + date: { type: String, required: true }, + description: { type: String, required: true }, + }, ], location: { - userProvided: { type: String, default: '' }, + userProvided: { type: String, default: "" }, coords: { - lat: { type: Number, default: '' }, - lng: { type: Number, default: '' }, + lat: { type: Number, default: "" }, + lng: { type: Number, default: "" }, }, - country: { type: String, default: '' }, - city: { type: String, default: '' }, - + country: { type: String, default: "" }, + city: { type: String, default: "" }, }, oldInfringements: [ - { date: { type: String, required: true }, description: { type: String, required: true } }, + { + date: { type: String, required: true }, + description: { type: String, required: true }, + }, ], privacySettings: { blueSquares: { type: Boolean, default: true }, @@ -104,7 +114,7 @@ const userProfileSchema = new Schema({ dueDate: { type: Date, required: true, - default: moment().tz('America/Los_Angeles').endOf('week'), + default: moment().tz("America/Los_Angeles").endOf("week"), }, summary: { type: String }, uploadDate: { type: Date }, @@ -134,17 +144,17 @@ const userProfileSchema = new Schema({ category: { type: String, enum: [ - 'Food', - 'Energy', - 'Housing', - 'Education', - 'Society', - 'Economics', - 'Stewardship', - 'Other', - 'Unspecified', + "Food", + "Energy", + "Housing", + "Education", + "Society", + "Economics", + "Stewardship", + "Other", + "Unspecified", ], - default: 'Other', + default: "Other", }, hrs: { type: Number, default: 0 }, }, @@ -152,48 +162,58 @@ const userProfileSchema = new Schema({ savedTangibleHrs: [Number], timeEntryEditHistory: [ { - date: { type: Date, required: true, default: moment().tz('America/Los_Angeles').toDate() }, + date: { + type: Date, + required: true, + default: moment().tz("America/Los_Angeles").toDate(), + }, initialSeconds: { type: Number, required: true }, newSeconds: { type: Number, required: true }, }, ], weeklySummaryNotReq: { type: Boolean, default: false }, - timeZone: { type: String, required: true, default: 'America/Los_Angeles' }, + timeZone: { type: String, required: true, default: "America/Los_Angeles" }, isVisible: { type: Boolean, default: false }, weeklySummaryOption: { type: String }, - bioPosted: { type: String, default: 'default' }, + bioPosted: { type: String, default: "default" }, isFirstTimelog: { type: Boolean, default: true }, teamCode: { type: String, - default: '', + default: "", validate: { validator(v) { const teamCoderegex = /^([a-zA-Z]-[a-zA-Z]{3}|[a-zA-Z]{5})$|^$/; return teamCoderegex.test(v); }, - message: - 'Please enter a code in the format of A-AAA or AAAAA', + message: "Please enter a code in the format of A-AAA or AAAAA", }, }, infoCollections: [ { areaName: { type: String }, areaContent: { type: String }, - }], + }, + ], + timeOffFrom: { type: Date, default: undefined }, + timeOffTill: { type: Date, default: undefined }, }); -userProfileSchema.pre('save', function (next) { +userProfileSchema.pre("save", function (next) { const user = this; - if (!user.isModified('password')) return next(); + if (!user.isModified("password")) return next(); return bcrypt .genSalt(SALT_ROUNDS) - .then(result => bcrypt.hash(user.password, result)) + .then((result) => bcrypt.hash(user.password, result)) .then((hash) => { user.password = hash; return next(); }) - .catch(error => next(error)); + .catch((error) => next(error)); }); -module.exports = mongoose.model('userProfile', userProfileSchema, 'userProfiles'); +module.exports = mongoose.model( + "userProfile", + userProfileSchema, + "userProfiles" +); From 02ba9860461b0e01b15bc68c0b89fa14bb2817df Mon Sep 17 00:00:00 2001 From: Shiwani Rajagopalan Date: Sun, 17 Dec 2023 14:16:00 -0500 Subject: [PATCH 26/36] added timeOffFrom and timeOffTill to taskHelper --- src/helpers/taskHelper.js | 420 +++++++++++++++++++++----------------- 1 file changed, 237 insertions(+), 183 deletions(-) diff --git a/src/helpers/taskHelper.js b/src/helpers/taskHelper.js index 800d1ee6b..fc6a331b3 100644 --- a/src/helpers/taskHelper.js +++ b/src/helpers/taskHelper.js @@ -1,149 +1,207 @@ -const moment = require('moment-timezone'); -const mongoose = require('mongoose'); -const userProfile = require('../models/userProfile'); -const timeentry = require('../models/timeentry'); -const myTeam = require('../helpers/helperModels/myTeam'); -const team = require('../models/team'); -const Task = require('../models/task'); -const TaskNotification = require('../models/taskNotification'); -const Wbs = require('../models/wbs'); +const moment = require("moment-timezone"); +const mongoose = require("mongoose"); +const userProfile = require("../models/userProfile"); +const timeentry = require("../models/timeentry"); +const myTeam = require("../helpers/helperModels/myTeam"); +const team = require("../models/team"); +const Task = require("../models/task"); +const TaskNotification = require("../models/taskNotification"); +const Wbs = require("../models/wbs"); const taskHelper = function () { const getTasksForTeams = async function (userId) { const userid = mongoose.Types.ObjectId(userId); - const userById = await userProfile.findOne({ _id: userid, isActive: true }, { - role: 1, firstName: 1, lastName: 1, role: 1, isVisible: 1, weeklycommittedHours: 1, weeklySummaries: 1, -}) - .then(res => res).catch((e) => {}); + const userById = await userProfile + .findOne( + { _id: userid, isActive: true }, + { + role: 1, + firstName: 1, + lastName: 1, + role: 1, + isVisible: 1, + weeklycommittedHours: 1, + weeklySummaries: 1, + } + ) + .then((res) => res) + .catch((e) => {}); if (userById == null) return null; const userRole = userById.role; const pdtstart = moment() - .tz('America/Los_Angeles') - .startOf('week') - .format('YYYY-MM-DD'); + .tz("America/Los_Angeles") + .startOf("week") + .format("YYYY-MM-DD"); const pdtend = moment() - .tz('America/Los_Angeles') - .endOf('week') - .format('YYYY-MM-DD'); + .tz("America/Los_Angeles") + .endOf("week") + .format("YYYY-MM-DD"); - let teamMemberIds = [userid]; - let teamMembers = []; + let teamMemberIds = [userid]; + let teamMembers = []; - if (userRole != 'Administrator' && userRole != 'Owner' && userRole != 'Core Team') // Manager , Mentor , Volunteer ... , Show only team members - { - const teamsResult = await team.find({ 'members.userId': { $in: [userid] } }, { members: 1 }) - .then(res => res).catch((e) => {}); + if ( + userRole != "Administrator" && + userRole != "Owner" && + userRole != "Core Team" + ) { + // Manager , Mentor , Volunteer ... , Show only team members + const teamsResult = await team + .find({ "members.userId": { $in: [userid] } }, { members: 1 }) + .then((res) => res) + .catch((e) => {}); - teamsResult.map((_myTeam) => { - _myTeam.members.map((teamMember) => { - if (!teamMember.userId.equals(userid)) teamMemberIds.push(teamMember.userId); - }); - }); + teamsResult.map((_myTeam) => { + _myTeam.members.map((teamMember) => { + if (!teamMember.userId.equals(userid)) + teamMemberIds.push(teamMember.userId); + }); + }); - teamMembers = await userProfile.find({ _id: { $in: teamMemberIds }, isActive: true }, - { - role: 1, firstName: 1, lastName: 1, weeklycommittedHours: 1, -}) - .then(res => res).catch((e) => {}); - } else if (userRole == 'Administrator') { // All users except Owner and Core Team - const excludedRoles = ['Core Team', 'Owner']; - teamMembers = await userProfile.find({ isActive: true, role: { $nin: excludedRoles } }, - { - role: 1, firstName: 1, lastName: 1, weeklycommittedHours: 1, -}) - .then(res => res).catch((e) => {}); - } else { // 'Core Team', 'Owner' //All users - teamMembers = await userProfile.find({ isActive: true }, - { - role: 1, firstName: 1, lastName: 1, weeklycommittedHours: 1, -}) - .then(res => res).catch((e) => {}); - } + teamMembers = await userProfile + .find( + { _id: { $in: teamMemberIds }, isActive: true }, + { + role: 1, + firstName: 1, + lastName: 1, + weeklycommittedHours: 1, + } + ) + .then((res) => res) + .catch((e) => {}); + } else if (userRole == "Administrator") { + // All users except Owner and Core Team + const excludedRoles = ["Core Team", "Owner"]; + teamMembers = await userProfile + .find( + { isActive: true, role: { $nin: excludedRoles } }, + { + role: 1, + firstName: 1, + lastName: 1, + weeklycommittedHours: 1, + } + ) + .then((res) => res) + .catch((e) => {}); + } else { + // 'Core Team', 'Owner' //All users + teamMembers = await userProfile + .find( + { isActive: true }, + { + role: 1, + firstName: 1, + lastName: 1, + weeklycommittedHours: 1, + } + ) + .then((res) => res) + .catch((e) => {}); + } - teamMemberIds = teamMembers.map(member => member._id); + teamMemberIds = teamMembers.map((member) => member._id); - const timeEntries = await timeentry.find({ - dateOfWork: { - $gte: pdtstart, - $lte: pdtend, - }, - personId: { $in: teamMemberIds }, - }); + const timeEntries = await timeentry.find({ + dateOfWork: { + $gte: pdtstart, + $lte: pdtend, + }, + personId: { $in: teamMemberIds }, + }); - const timeEntryByPerson = {}; - timeEntries.map((timeEntry) => { - const personIdStr = timeEntry.personId.toString(); + const timeEntryByPerson = {}; + timeEntries.map((timeEntry) => { + const personIdStr = timeEntry.personId.toString(); - if (timeEntryByPerson[personIdStr] == null) { timeEntryByPerson[personIdStr] = { tangibleSeconds: 0, intangibleSeconds: 0, totalSeconds: 0 }; } + if (timeEntryByPerson[personIdStr] == null) { + timeEntryByPerson[personIdStr] = { + tangibleSeconds: 0, + intangibleSeconds: 0, + totalSeconds: 0, + }; + } - if (timeEntry.isTangible === true) { - timeEntryByPerson[personIdStr].tangibleSeconds += timeEntry.totalSeconds; - } - timeEntryByPerson[personIdStr].totalSeconds += timeEntry.totalSeconds; - }); + if (timeEntry.isTangible === true) { + timeEntryByPerson[personIdStr].tangibleSeconds += + timeEntry.totalSeconds; + } + timeEntryByPerson[personIdStr].totalSeconds += timeEntry.totalSeconds; + }); - const teamMemberTasks = await Task.find({ 'resources.userID': { $in: teamMemberIds } }, { 'resources.profilePic': 0 }) - .populate({ - path: 'wbsId', - select: 'projectId', - }); - const teamMemberTaskIds = teamMemberTasks.map(task => task._id); - const teamMemberTaskNotifications = await TaskNotification.find({ taskId: { $in: teamMemberTaskIds } }); + const teamMemberTasks = await Task.find( + { "resources.userID": { $in: teamMemberIds } }, + { "resources.profilePic": 0 } + ).populate({ + path: "wbsId", + select: "projectId", + }); + const teamMemberTaskIds = teamMemberTasks.map((task) => task._id); + const teamMemberTaskNotifications = await TaskNotification.find({ + taskId: { $in: teamMemberTaskIds }, + }); - const taskNotificationByTaskNdUser = []; - teamMemberTaskNotifications.map((teamMemberTaskNotification) => { - const taskIdStr = teamMemberTaskNotification.taskId.toString(); - const userIdStr = teamMemberTaskNotification.userId.toString(); - const taskNdUserID = `${taskIdStr},${userIdStr}`; + const taskNotificationByTaskNdUser = []; + teamMemberTaskNotifications.map((teamMemberTaskNotification) => { + const taskIdStr = teamMemberTaskNotification.taskId.toString(); + const userIdStr = teamMemberTaskNotification.userId.toString(); + const taskNdUserID = `${taskIdStr},${userIdStr}`; - if (taskNotificationByTaskNdUser[taskNdUserID]) { - taskNotificationByTaskNdUser[taskNdUserID].push(teamMemberTaskNotification); - } else { - taskNotificationByTaskNdUser[taskNdUserID] = [teamMemberTaskNotification]; - } - }); + if (taskNotificationByTaskNdUser[taskNdUserID]) { + taskNotificationByTaskNdUser[taskNdUserID].push( + teamMemberTaskNotification + ); + } else { + taskNotificationByTaskNdUser[taskNdUserID] = [ + teamMemberTaskNotification, + ]; + } + }); - const taskByPerson = []; + const taskByPerson = []; - teamMemberTasks.map((teamMemberTask) => { - const projId = teamMemberTask.wbsId?.projectId; - const _teamMemberTask = { ...teamMemberTask._doc }; - _teamMemberTask.projectId = projId; - const taskIdStr = _teamMemberTask._id.toString(); + teamMemberTasks.map((teamMemberTask) => { + const projId = teamMemberTask.wbsId?.projectId; + const _teamMemberTask = { ...teamMemberTask._doc }; + _teamMemberTask.projectId = projId; + const taskIdStr = _teamMemberTask._id.toString(); - teamMemberTask.resources.map((resource) => { - const resourceIdStr = resource.userID.toString(); - const taskNdUserID = `${taskIdStr},${resourceIdStr}`; - _teamMemberTask.taskNotifications = taskNotificationByTaskNdUser[taskNdUserID] || []; - if (taskByPerson[resourceIdStr]) { - taskByPerson[resourceIdStr].push(_teamMemberTask); - } else { - taskByPerson[resourceIdStr] = [_teamMemberTask]; - } - }); + teamMemberTask.resources.map((resource) => { + const resourceIdStr = resource.userID.toString(); + const taskNdUserID = `${taskIdStr},${resourceIdStr}`; + _teamMemberTask.taskNotifications = + taskNotificationByTaskNdUser[taskNdUserID] || []; + if (taskByPerson[resourceIdStr]) { + taskByPerson[resourceIdStr].push(_teamMemberTask); + } else { + taskByPerson[resourceIdStr] = [_teamMemberTask]; + } }); - + }); const teamMemberTasksData = []; teamMembers.map((teamMember) => { - const obj = { - personId: teamMember._id, - role: teamMember.role, - name: `${teamMember.firstName } ${ teamMember.lastName}`, - weeklycommittedHours: teamMember.weeklycommittedHours, - totaltangibletime_hrs: ((timeEntryByPerson[teamMember._id.toString()]?.tangibleSeconds / 3600) || 0), - totaltime_hrs: ((timeEntryByPerson[teamMember._id.toString()]?.totalSeconds / 3600) || 0), - tasks: taskByPerson[teamMember._id.toString()] || [], - }; - teamMemberTasksData.push(obj); + const obj = { + personId: teamMember._id, + role: teamMember.role, + name: `${teamMember.firstName} ${teamMember.lastName}`, + weeklycommittedHours: teamMember.weeklycommittedHours, + totaltangibletime_hrs: + timeEntryByPerson[teamMember._id.toString()]?.tangibleSeconds / + 3600 || 0, + totaltime_hrs: + timeEntryByPerson[teamMember._id.toString()]?.totalSeconds / 3600 || + 0, + tasks: taskByPerson[teamMember._id.toString()] || [], + }; + teamMemberTasksData.push(obj); }); - return teamMemberTasksData; - // return myteam.aggregate([ // { // $match: { @@ -429,13 +487,13 @@ const taskHelper = function () { }; const getTasksForSingleUser = function (userId) { const pdtstart = moment() - .tz('America/Los_Angeles') - .startOf('week') - .format('YYYY-MM-DD'); + .tz("America/Los_Angeles") + .startOf("week") + .format("YYYY-MM-DD"); const pdtend = moment() - .tz('America/Los_Angeles') - .endOf('week') - .format('YYYY-MM-DD'); + .tz("America/Los_Angeles") + .endOf("week") + .format("YYYY-MM-DD"); return userProfile.aggregate([ { $match: { @@ -444,20 +502,16 @@ const taskHelper = function () { }, { $project: { - personId: '$_id', - role: '$role', + personId: "$_id", + role: "$role", name: { - $concat: [ - '$firstName', - ' ', - '$lastName', - ], + $concat: ["$firstName", " ", "$lastName"], }, weeklycommittedHours: { $sum: [ - '$weeklycommittedHours', + "$weeklycommittedHours", { - $ifNull: ['$missedHours', 0], + $ifNull: ["$missedHours", 0], }, ], }, @@ -465,10 +519,10 @@ const taskHelper = function () { }, { $lookup: { - from: 'timeEntries', - localField: 'personId', - foreignField: 'personId', - as: 'timeEntryData', + from: "timeEntries", + localField: "personId", + foreignField: "personId", + as: "timeEntryData", }, }, { @@ -479,18 +533,18 @@ const taskHelper = function () { role: 1, timeEntryData: { $filter: { - input: '$timeEntryData', - as: 'timeentry', + input: "$timeEntryData", + as: "timeentry", cond: { $and: [ { - $gte: ['$$timeentry.dateOfWork', pdtstart], + $gte: ["$$timeentry.dateOfWork", pdtstart], }, { - $lte: ['$$timeentry.dateOfWork', pdtend], + $lte: ["$$timeentry.dateOfWork", pdtend], }, { - $in: ['$$timeentry.entryType', ['default', null]], + $in: ["$$timeentry.entryType", ["default", null]], }, ], }, @@ -500,7 +554,7 @@ const taskHelper = function () { }, { $unwind: { - path: '$timeEntryData', + path: "$timeEntryData", preserveNullAndEmptyArrays: true, }, }, @@ -513,18 +567,18 @@ const taskHelper = function () { totalSeconds: { $cond: [ { - $gte: ['$timeEntryData.totalSeconds', 0], + $gte: ["$timeEntryData.totalSeconds", 0], }, - '$timeEntryData.totalSeconds', + "$timeEntryData.totalSeconds", 0, ], }, isTangible: { $cond: [ { - $gte: ['$timeEntryData.totalSeconds', 0], + $gte: ["$timeEntryData.totalSeconds", 0], }, - '$timeEntryData.isTangible', + "$timeEntryData.isTangible", false, ], }, @@ -535,9 +589,9 @@ const taskHelper = function () { tangibletime: { $cond: [ { - $eq: ['$isTangible', true], + $eq: ["$isTangible", true], }, - '$totalSeconds', + "$totalSeconds", 0, ], }, @@ -546,40 +600,40 @@ const taskHelper = function () { { $group: { _id: { - personId: '$personId', - weeklycommittedHours: '$weeklycommittedHours', - name: '$name', - role: '$role', + personId: "$personId", + weeklycommittedHours: "$weeklycommittedHours", + name: "$name", + role: "$role", }, totalSeconds: { - $sum: '$totalSeconds', + $sum: "$totalSeconds", }, tangibletime: { - $sum: '$tangibletime', + $sum: "$tangibletime", }, }, }, { $project: { _id: 0, - personId: '$_id.personId', - name: '$_id.name', - weeklycommittedHours: '$_id.weeklycommittedHours', - role: '$_id.role', + personId: "$_id.personId", + name: "$_id.name", + weeklycommittedHours: "$_id.weeklycommittedHours", + role: "$_id.role", totaltime_hrs: { - $divide: ['$totalSeconds', 3600], + $divide: ["$totalSeconds", 3600], }, totaltangibletime_hrs: { - $divide: ['$tangibletime', 3600], + $divide: ["$tangibletime", 3600], }, }, }, { $lookup: { - from: 'tasks', - localField: 'personId', - foreignField: 'resources.userID', - as: 'tasks', + from: "tasks", + localField: "personId", + foreignField: "resources.userID", + as: "tasks", }, }, { @@ -593,25 +647,25 @@ const taskHelper = function () { }, { $unwind: { - path: '$tasks', + path: "$tasks", preserveNullAndEmptyArrays: true, }, }, { $lookup: { - from: 'wbs', - localField: 'tasks.wbsId', - foreignField: '_id', - as: 'projectId', + from: "wbs", + localField: "tasks.wbsId", + foreignField: "_id", + as: "projectId", }, }, { $addFields: { - 'tasks.projectId': { + "tasks.projectId": { $cond: [ - { $ne: ['$projectId', []] }, - { $arrayElemAt: ['$projectId', 0] }, - '$tasks.projectId', + { $ne: ["$projectId", []] }, + { $arrayElemAt: ["$projectId", 0] }, + "$tasks.projectId", ], }, }, @@ -633,40 +687,40 @@ const taskHelper = function () { }, { $addFields: { - 'tasks.projectId': '$tasks.projectId.projectId', + "tasks.projectId": "$tasks.projectId.projectId", }, }, { $lookup: { - from: 'taskNotifications', - localField: 'tasks._id', - foreignField: 'taskId', - as: 'tasks.taskNotifications', + from: "taskNotifications", + localField: "tasks._id", + foreignField: "taskId", + as: "tasks.taskNotifications", }, }, { $group: { - _id: '$personId', - tasks: { $push: '$tasks' }, + _id: "$personId", + tasks: { $push: "$tasks" }, data: { - $first: '$$ROOT', + $first: "$$ROOT", }, }, }, { $addFields: { - 'data.tasks': { + "data.tasks": { $filter: { - input: '$tasks', - as: 'task', - cond: { $ne: ['$$task', {}] }, + input: "$tasks", + as: "task", + cond: { $ne: ["$$task", {}] }, }, }, }, }, { $replaceRoot: { - newRoot: '$data', + newRoot: "$data", }, }, ]); @@ -674,7 +728,7 @@ const taskHelper = function () { const getUserProfileFirstAndLastName = function (userId) { return userProfile.findById(userId).then((results) => { if (!results) { - return ' '; + return " "; } return `${results.firstName} ${results.lastName}`; }); From c26046ec38728b000962cfd42dd7c71ca1fe24c1 Mon Sep 17 00:00:00 2001 From: Shiwani Rajagopalan Date: Sun, 17 Dec 2023 14:55:58 -0500 Subject: [PATCH 27/36] added timeOffFrom and timeOffTill to reportHelper --- src/helpers/reporthelper.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/helpers/reporthelper.js b/src/helpers/reporthelper.js index 3826aa8ed..eac0a71a1 100644 --- a/src/helpers/reporthelper.js +++ b/src/helpers/reporthelper.js @@ -121,7 +121,13 @@ const reporthelper = function () { }, }, teamCode: { - $ifNull: ['$teamCode', ''], + $ifNull: ["$teamCode", ""], + }, + timeOffFrom: { + $ifNull: ["$timeOffFrom", null], + }, + timeOffTill: { + $ifNull: ["$timeOffTill", null], }, role: 1, weeklySummaries: { From a153942136a951dde7b4503a58f52218087ffe33 Mon Sep 17 00:00:00 2001 From: Shiwani Rajagopalan Date: Mon, 18 Dec 2023 14:32:17 -0500 Subject: [PATCH 28/36] updated dashboardhelper and taskHelper to add timeOffFrom anf timeOffTill for individual task and leaderboard data --- src/helpers/dashboardhelper.js | 2 ++ src/helpers/taskHelper.js | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/helpers/dashboardhelper.js b/src/helpers/dashboardhelper.js index a200a0095..0482f4252 100644 --- a/src/helpers/dashboardhelper.js +++ b/src/helpers/dashboardhelper.js @@ -638,6 +638,8 @@ const dashboardhelper = function () { totalintangibletime_hrs: intangibleSeconds / 3600, percentagespentintangible: (intangibleSeconds / tangibleSeconds) * 100, + timeOffFrom: user.timeOffFrom, + timeOffTill: user.timeOffTill, }, ]; } catch (err) { diff --git a/src/helpers/taskHelper.js b/src/helpers/taskHelper.js index fc6a331b3..6d5449bc8 100644 --- a/src/helpers/taskHelper.js +++ b/src/helpers/taskHelper.js @@ -515,6 +515,12 @@ const taskHelper = function () { }, ], }, + timeOffFrom: { + $ifNull: ["$timeOffFrom", null], + }, + timeOffTill: { + $ifNull: ["$timeOffTill", null], + }, }, }, { @@ -530,6 +536,8 @@ const taskHelper = function () { personId: 1, name: 1, weeklycommittedHours: 1, + timeOffFrom: 1, + timeOffTill: 1, role: 1, timeEntryData: { $filter: { @@ -563,6 +571,8 @@ const taskHelper = function () { personId: 1, name: 1, weeklycommittedHours: 1, + timeOffFrom: 1, + timeOffTill: 1, role: 1, totalSeconds: { $cond: [ @@ -602,6 +612,8 @@ const taskHelper = function () { _id: { personId: "$personId", weeklycommittedHours: "$weeklycommittedHours", + timeOffFrom: "$timeOffFrom", + timeOffTill: "$timeOffTill", name: "$name", role: "$role", }, @@ -619,6 +631,8 @@ const taskHelper = function () { personId: "$_id.personId", name: "$_id.name", weeklycommittedHours: "$_id.weeklycommittedHours", + timeOffFrom: "$_id.timeOffFrom", + timeOffTill: "$_id.timeOffTill", role: "$_id.role", totaltime_hrs: { $divide: ["$totalSeconds", 3600], From f484767ccef78e1e8e6118f8323003241c9daa1d Mon Sep 17 00:00:00 2001 From: Shiwani Rajagopalan Date: Mon, 18 Dec 2023 17:50:04 -0500 Subject: [PATCH 29/36] modified the array representation for timeOffFrom and TimeOffTill to a value instead of array --- src/helpers/dashboardhelper.js | 265 +++++++++++++++++++++++++++++++++ 1 file changed, 265 insertions(+) diff --git a/src/helpers/dashboardhelper.js b/src/helpers/dashboardhelper.js index 0482f4252..e6d5118f6 100644 --- a/src/helpers/dashboardhelper.js +++ b/src/helpers/dashboardhelper.js @@ -258,6 +258,7 @@ const dashboardhelper = function () { $gte: pdtstart, $lte: pdtend, }, +<<<<<<< HEAD personId: { $in: teamMemberIds }, }); @@ -582,6 +583,270 @@ const dashboardhelper = function () { // }, // }, // ]); +======= + { + $unwind: "$myteam", + }, + { + $project: { + _id: 0, + role: 1, + personId: "$myteam._id", + name: "$myteam.fullName", + }, + }, + { + $lookup: { + from: "userProfiles", + localField: "personId", + foreignField: "_id", + as: "persondata", + }, + }, + { + $match: { + // leaderboard user roles hierarchy + $or: [ + { + role: { $in: ["Owner", "Core Team"] }, + }, + { + $and: [ + { + role: "Administrator", + }, + { "persondata.0.role": { $nin: ["Owner", "Administrator"] } }, + ], + }, + { + $and: [ + { + role: { $in: ["Manager", "Mentor"] }, + }, + { + "persondata.0.role": { + $nin: [ + "Manager", + "Mentor", + "Core Team", + "Administrator", + "Owner", + ], + }, + }, + ], + }, + { "persondata.0._id": userId }, + { "persondata.0.role": "Volunteer" }, + { "persondata.0.isVisible": true }, + ], + }, + }, + { + $project: { + personId: 1, + name: 1, + role: { + $arrayElemAt: ["$persondata.role", 0], + }, + isVisible: { + $arrayElemAt: ["$persondata.isVisible", 0], + }, + hasSummary: { + $ne: [ + { + $arrayElemAt: [ + { + $arrayElemAt: ["$persondata.weeklySummaries.summary", 0], + }, + 0, + ], + }, + "", + ], + }, + weeklycommittedHours: { + $sum: [ + { + $arrayElemAt: ["$persondata.weeklycommittedHours", 0], + }, + { + $ifNull: [{ $arrayElemAt: ["$persondata.missedHours", 0] }, 0], + }, + ], + }, + timeOffFrom: { + $ifNull: [{ $arrayElemAt: ["$persondata.timeOffFrom", 0] }, null], + }, + timeOffTill: { + $ifNull: [{ $arrayElemAt: ["$persondata.timeOffTill", 0] }, null], + }, + }, + }, + { + $lookup: { + from: "timeEntries", + localField: "personId", + foreignField: "personId", + as: "timeEntryData", + }, + }, + { + $project: { + personId: 1, + name: 1, + role: 1, + isVisible: 1, + hasSummary: 1, + weeklycommittedHours: 1, + timeOffFrom: 1, + timeOffTill: 1, + timeEntryData: { + $filter: { + input: "$timeEntryData", + as: "timeentry", + cond: { + $and: [ + { + $gte: ["$$timeentry.dateOfWork", pdtstart], + }, + { + $lte: ["$$timeentry.dateOfWork", pdtend], + }, + ], + }, + }, + }, + }, + }, + { + $unwind: { + path: "$timeEntryData", + preserveNullAndEmptyArrays: true, + }, + }, + { + $project: { + personId: 1, + name: 1, + role: 1, + isVisible: 1, + hasSummary: 1, + weeklycommittedHours: 1, + timeOffFrom: 1, + timeOffTill: 1, + totalSeconds: { + $cond: [ + { + $gte: ["$timeEntryData.totalSeconds", 0], + }, + "$timeEntryData.totalSeconds", + 0, + ], + }, + isTangible: { + $cond: [ + { + $gte: ["$timeEntryData.totalSeconds", 0], + }, + "$timeEntryData.isTangible", + false, + ], + }, + }, + }, + { + $addFields: { + tangibletime: { + $cond: [ + { + $eq: ["$isTangible", true], + }, + "$totalSeconds", + 0, + ], + }, + intangibletime: { + $cond: [ + { + $eq: ["$isTangible", false], + }, + "$totalSeconds", + 0, + ], + }, + }, + }, + { + $group: { + _id: { + personId: "$personId", + weeklycommittedHours: "$weeklycommittedHours", + timeOffFrom: "$timeOffFrom", + timeOffTill: "$timeOffTill", + name: "$name", + role: "$role", + isVisible: "$isVisible", + hasSummary: "$hasSummary", + }, + totalSeconds: { + $sum: "$totalSeconds", + }, + tangibletime: { + $sum: "$tangibletime", + }, + intangibletime: { + $sum: "$intangibletime", + }, + }, + }, + { + $project: { + _id: 0, + personId: "$_id.personId", + name: "$_id.name", + role: "$_id.role", + isVisible: "$_id.isVisible", + hasSummary: "$_id.hasSummary", + weeklycommittedHours: "$_id.weeklycommittedHours", + totaltime_hrs: { + $divide: ["$totalSeconds", 3600], + }, + totaltangibletime_hrs: { + $divide: ["$tangibletime", 3600], + }, + totalintangibletime_hrs: { + $divide: ["$intangibletime", 3600], + }, + percentagespentintangible: { + $cond: [ + { + $eq: ["$totalSeconds", 0], + }, + 0, + { + $multiply: [ + { + $divide: ["$tangibletime", "$totalSeconds"], + }, + 100, + ], + }, + ], + }, + timeOffFrom: "$_id.timeOffFrom", + timeOffTill: "$_id.timeOffTill", + }, + }, + { + $sort: { + totaltangibletime_hrs: -1, + name: 1, + role: 1, + }, + }, + ]); +>>>>>>> 367abce (modified the array representation for timeOffFrom and TimeOffTill to a value instead of array) }; /** From a956753af7579b5049338493273f19cefb419f0d Mon Sep 17 00:00:00 2001 From: Shiwani99 <78906820+Shiwani99@users.noreply.github.com> Date: Mon, 18 Dec 2023 18:53:26 -0500 Subject: [PATCH 30/36] Added comments to reasonSchedulingController.js --- src/controllers/reasonSchedulingController.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/controllers/reasonSchedulingController.js b/src/controllers/reasonSchedulingController.js index 4d56c5a5e..63dc51fed 100644 --- a/src/controllers/reasonSchedulingController.js +++ b/src/controllers/reasonSchedulingController.js @@ -55,12 +55,13 @@ const postReason = async (req, res) => { }); } - // new changes + // conditions added to check if timeOffFrom and timeOffTill fields existed if ( foundUser.hasOwnProperty("timeOffFrom") && foundUser.hasOwnProperty("timeOffTill") ) { + // if currentDate is greater than or equal to the last timeOffTill date then both the fields will be updated if (currentDate >= foundUser.timeOffTill) { await UserModel.findOneAndUpdate( { @@ -74,6 +75,7 @@ const postReason = async (req, res) => { } ); } else { + // else only timeOffTill will be updated await UserModel.findOneAndUpdate( { _id: userId, @@ -86,6 +88,7 @@ const postReason = async (req, res) => { ); } } else { + // if both the fields are not present then these fields will be added to mongoDB for that user await UserModel.findOneAndUpdate( { _id: userId, From 6346f4a40093db72230a5a4a673b50a3bd3324be Mon Sep 17 00:00:00 2001 From: Shiwani Rajagopalan Date: Mon, 8 Jan 2024 06:04:11 -0500 Subject: [PATCH 31/36] fixed issues with rebasing current branch with development and added timeOffFrom and timeOffTill to the new aggregation in dashboard and task helper --- package-lock.json | 106 ++++++------- src/helpers/dashboardhelper.js | 273 +-------------------------------- src/helpers/taskHelper.js | 10 ++ 3 files changed, 71 insertions(+), 318 deletions(-) diff --git a/package-lock.json b/package-lock.json index ff66994e8..ecce49ec7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1568,7 +1568,7 @@ "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, "@types/mime": { @@ -1729,7 +1729,7 @@ "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, "array-includes": { "version": "3.1.6", @@ -2768,7 +2768,7 @@ "bcryptjs": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", - "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=" + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" }, "bignumber.js": { "version": "9.0.2", @@ -2863,7 +2863,7 @@ "buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" }, "buffer-from": { "version": "1.1.2", @@ -2995,7 +2995,7 @@ "clone": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=" + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==" }, "clone-deep": { "version": "4.0.1", @@ -3039,12 +3039,12 @@ "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "concat-stream": { "version": "1.6.2", @@ -3100,7 +3100,7 @@ "cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "core-js": { "version": "3.21.1", @@ -3253,7 +3253,7 @@ "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "electron-to-chromium": { "version": "1.4.81", @@ -3269,7 +3269,7 @@ "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" }, "es-abstract": { "version": "1.19.1", @@ -3350,7 +3350,7 @@ "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, "escape-string-regexp": { "version": "1.0.5", @@ -4264,7 +4264,7 @@ "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" }, "event-target-shim": { "version": "5.0.1", @@ -4445,7 +4445,7 @@ "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, "fast-text-encoding": { @@ -4575,7 +4575,7 @@ "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" }, "fs-readdir-recursive": { "version": "1.1.0", @@ -4585,7 +4585,7 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "fsevents": { "version": "2.3.2", @@ -4968,7 +4968,7 @@ "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true }, "indent-string": { @@ -4980,7 +4980,7 @@ "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "requires": { "once": "^1.3.0", "wrappy": "1" @@ -5085,7 +5085,7 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" }, "is-glob": { "version": "4.0.3", @@ -5187,13 +5187,13 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==" }, "js-tokens": { "version": "4.0.0", @@ -5231,7 +5231,7 @@ "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, "json5": { @@ -5552,7 +5552,7 @@ "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" }, "lodash.merge": { "version": "4.6.2", @@ -5729,7 +5729,7 @@ "lru_map": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", - "integrity": "sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0=" + "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==" }, "make-dir": { "version": "2.1.0", @@ -5743,7 +5743,7 @@ "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" }, "memory-pager": { "version": "1.5.0", @@ -5754,7 +5754,7 @@ "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" }, "merge-stream": { "version": "2.0.0", @@ -5765,7 +5765,7 @@ "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" }, "micromatch": { "version": "4.0.5", @@ -5945,7 +5945,7 @@ "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, "negotiator": { @@ -6085,7 +6085,7 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" }, "object-inspect": { "version": "1.12.0", @@ -6804,7 +6804,7 @@ "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "requires": { "wrappy": "1" } @@ -6834,7 +6834,7 @@ "os-shim": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz", - "integrity": "sha1-a2LDeRz3kJ6jXtRuF2WLtBfLORc=", + "integrity": "sha512-jd0cvB8qQ5uVt0lvCIexBaROw1KyKm5sbulg2fWOHjETisuCzWyt+eTZKEMs8v6HwzoGs8xik26jg7eCM6pS+A==", "dev": true }, "p-limit": { @@ -6879,7 +6879,7 @@ "parse-passwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=" + "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==" }, "parseurl": { "version": "1.3.3", @@ -6894,7 +6894,7 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" }, "path-key": { "version": "3.1.1", @@ -6910,7 +6910,7 @@ "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, "picocolors": { "version": "1.0.0", @@ -6957,7 +6957,7 @@ "pre-commit": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/pre-commit/-/pre-commit-1.2.2.tgz", - "integrity": "sha1-287g7p3nI15X95xW186UZBpp7sY=", + "integrity": "sha512-qokTiqxD6GjODy5ETAIgzsRgnBWWQHQH2ghy86PU7mIn/wuWeTwF3otyNQZxWBwVn8XNr8Tdzj/QfUXpH+gRZA==", "dev": true, "requires": { "cross-spawn": "^5.0.1", @@ -6968,7 +6968,7 @@ "cross-spawn": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", "dev": true, "requires": { "lru-cache": "^4.0.1", @@ -6979,7 +6979,7 @@ "which": { "version": "1.2.14", "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", - "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=", + "integrity": "sha512-16uPglFkRPzgiUXYMi1Jf8Z5EzN1iB4V0ZtMXcHZnwsBtQhhHeCqoWw7tsUY42hJGNDWtUsVLTjakIa5BgAxCw==", "dev": true, "requires": { "isexe": "^2.0.0" @@ -7021,7 +7021,7 @@ "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", "dev": true }, "pstree.remy": { @@ -7499,7 +7499,7 @@ "sparse-bitfield": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", - "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", "optional": true, "requires": { "memory-pager": "^1.0.2" @@ -7508,7 +7508,7 @@ "spawn-sync": { "version": "1.0.15", "resolved": "https://registry.npmjs.org/spawn-sync/-/spawn-sync-1.0.15.tgz", - "integrity": "sha1-sAeZVX63+wyDdsKdROih6mfldHY=", + "integrity": "sha512-9DWBgrgYZzNghseho0JOuh+5fg9u6QWhAWa51QC7+U5rCheZ/j1DrEZnyE0RBBRqZ9uEXGPgSSM0nky6burpVw==", "dev": true, "requires": { "concat-stream": "^1.4.7", @@ -7903,7 +7903,7 @@ "strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true }, "strip-final-newline": { @@ -7934,19 +7934,19 @@ "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==" }, "to-regex-range": { "version": "5.0.1", @@ -7973,7 +7973,7 @@ "tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "tsconfig-paths": { "version": "3.14.2", @@ -8091,7 +8091,7 @@ "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", "dev": true }, "unbox-primitive": { @@ -8138,7 +8138,7 @@ "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" }, "uri-js": { "version": "4.4.1", @@ -8152,17 +8152,17 @@ "url-template": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", - "integrity": "sha1-/FZaPMy/93MMd19WQflVV5FDnyE=" + "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==" }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" }, "uuid": { "version": "3.4.0", @@ -8185,17 +8185,17 @@ "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" }, "webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "requires": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -8313,7 +8313,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "ws": { "version": "8.8.1", diff --git a/src/helpers/dashboardhelper.js b/src/helpers/dashboardhelper.js index e6d5118f6..9335a67c4 100644 --- a/src/helpers/dashboardhelper.js +++ b/src/helpers/dashboardhelper.js @@ -212,6 +212,8 @@ const dashboardhelper = function () { isVisible: 1, weeklycommittedHours: 1, weeklySummaries: 1, + timeOffFrom: 1, + timeOffTill: 1, } ) .then((res) => res) @@ -229,6 +231,8 @@ const dashboardhelper = function () { isVisible: 1, weeklycommittedHours: 1, weeklySummaries: 1, + timeOffFrom: 1, + timeOffTill: 1, } ) .then((res) => res) @@ -245,6 +249,8 @@ const dashboardhelper = function () { isVisible: 1, weeklycommittedHours: 1, weeklySummaries: 1, + timeOffFrom: 1, + timeOffTill: 1, } ) .then((res) => res) @@ -258,7 +264,6 @@ const dashboardhelper = function () { $gte: pdtstart, $lte: pdtend, }, -<<<<<<< HEAD personId: { $in: teamMemberIds }, }); @@ -314,6 +319,8 @@ const dashboardhelper = function () { timeEntryByPerson[teamMember._id.toString()]?.totalSeconds) * 100 : 0, + timeOffFrom: teamMember.timeOffFrom || null, + timeOffTill: teamMember.timeOffTill || null, }; leaderBoardData.push(obj); }); @@ -583,270 +590,6 @@ const dashboardhelper = function () { // }, // }, // ]); -======= - { - $unwind: "$myteam", - }, - { - $project: { - _id: 0, - role: 1, - personId: "$myteam._id", - name: "$myteam.fullName", - }, - }, - { - $lookup: { - from: "userProfiles", - localField: "personId", - foreignField: "_id", - as: "persondata", - }, - }, - { - $match: { - // leaderboard user roles hierarchy - $or: [ - { - role: { $in: ["Owner", "Core Team"] }, - }, - { - $and: [ - { - role: "Administrator", - }, - { "persondata.0.role": { $nin: ["Owner", "Administrator"] } }, - ], - }, - { - $and: [ - { - role: { $in: ["Manager", "Mentor"] }, - }, - { - "persondata.0.role": { - $nin: [ - "Manager", - "Mentor", - "Core Team", - "Administrator", - "Owner", - ], - }, - }, - ], - }, - { "persondata.0._id": userId }, - { "persondata.0.role": "Volunteer" }, - { "persondata.0.isVisible": true }, - ], - }, - }, - { - $project: { - personId: 1, - name: 1, - role: { - $arrayElemAt: ["$persondata.role", 0], - }, - isVisible: { - $arrayElemAt: ["$persondata.isVisible", 0], - }, - hasSummary: { - $ne: [ - { - $arrayElemAt: [ - { - $arrayElemAt: ["$persondata.weeklySummaries.summary", 0], - }, - 0, - ], - }, - "", - ], - }, - weeklycommittedHours: { - $sum: [ - { - $arrayElemAt: ["$persondata.weeklycommittedHours", 0], - }, - { - $ifNull: [{ $arrayElemAt: ["$persondata.missedHours", 0] }, 0], - }, - ], - }, - timeOffFrom: { - $ifNull: [{ $arrayElemAt: ["$persondata.timeOffFrom", 0] }, null], - }, - timeOffTill: { - $ifNull: [{ $arrayElemAt: ["$persondata.timeOffTill", 0] }, null], - }, - }, - }, - { - $lookup: { - from: "timeEntries", - localField: "personId", - foreignField: "personId", - as: "timeEntryData", - }, - }, - { - $project: { - personId: 1, - name: 1, - role: 1, - isVisible: 1, - hasSummary: 1, - weeklycommittedHours: 1, - timeOffFrom: 1, - timeOffTill: 1, - timeEntryData: { - $filter: { - input: "$timeEntryData", - as: "timeentry", - cond: { - $and: [ - { - $gte: ["$$timeentry.dateOfWork", pdtstart], - }, - { - $lte: ["$$timeentry.dateOfWork", pdtend], - }, - ], - }, - }, - }, - }, - }, - { - $unwind: { - path: "$timeEntryData", - preserveNullAndEmptyArrays: true, - }, - }, - { - $project: { - personId: 1, - name: 1, - role: 1, - isVisible: 1, - hasSummary: 1, - weeklycommittedHours: 1, - timeOffFrom: 1, - timeOffTill: 1, - totalSeconds: { - $cond: [ - { - $gte: ["$timeEntryData.totalSeconds", 0], - }, - "$timeEntryData.totalSeconds", - 0, - ], - }, - isTangible: { - $cond: [ - { - $gte: ["$timeEntryData.totalSeconds", 0], - }, - "$timeEntryData.isTangible", - false, - ], - }, - }, - }, - { - $addFields: { - tangibletime: { - $cond: [ - { - $eq: ["$isTangible", true], - }, - "$totalSeconds", - 0, - ], - }, - intangibletime: { - $cond: [ - { - $eq: ["$isTangible", false], - }, - "$totalSeconds", - 0, - ], - }, - }, - }, - { - $group: { - _id: { - personId: "$personId", - weeklycommittedHours: "$weeklycommittedHours", - timeOffFrom: "$timeOffFrom", - timeOffTill: "$timeOffTill", - name: "$name", - role: "$role", - isVisible: "$isVisible", - hasSummary: "$hasSummary", - }, - totalSeconds: { - $sum: "$totalSeconds", - }, - tangibletime: { - $sum: "$tangibletime", - }, - intangibletime: { - $sum: "$intangibletime", - }, - }, - }, - { - $project: { - _id: 0, - personId: "$_id.personId", - name: "$_id.name", - role: "$_id.role", - isVisible: "$_id.isVisible", - hasSummary: "$_id.hasSummary", - weeklycommittedHours: "$_id.weeklycommittedHours", - totaltime_hrs: { - $divide: ["$totalSeconds", 3600], - }, - totaltangibletime_hrs: { - $divide: ["$tangibletime", 3600], - }, - totalintangibletime_hrs: { - $divide: ["$intangibletime", 3600], - }, - percentagespentintangible: { - $cond: [ - { - $eq: ["$totalSeconds", 0], - }, - 0, - { - $multiply: [ - { - $divide: ["$tangibletime", "$totalSeconds"], - }, - 100, - ], - }, - ], - }, - timeOffFrom: "$_id.timeOffFrom", - timeOffTill: "$_id.timeOffTill", - }, - }, - { - $sort: { - totaltangibletime_hrs: -1, - name: 1, - role: 1, - }, - }, - ]); ->>>>>>> 367abce (modified the array representation for timeOffFrom and TimeOffTill to a value instead of array) }; /** diff --git a/src/helpers/taskHelper.js b/src/helpers/taskHelper.js index 6d5449bc8..8347a56ef 100644 --- a/src/helpers/taskHelper.js +++ b/src/helpers/taskHelper.js @@ -22,6 +22,8 @@ const taskHelper = function () { isVisible: 1, weeklycommittedHours: 1, weeklySummaries: 1, + timeOffFrom: 1, + timeOffTill: 1, } ) .then((res) => res) @@ -68,6 +70,8 @@ const taskHelper = function () { firstName: 1, lastName: 1, weeklycommittedHours: 1, + timeOffFrom: 1, + timeOffTill: 1, } ) .then((res) => res) @@ -83,6 +87,8 @@ const taskHelper = function () { firstName: 1, lastName: 1, weeklycommittedHours: 1, + timeOffFrom: 1, + timeOffTill: 1, } ) .then((res) => res) @@ -97,6 +103,8 @@ const taskHelper = function () { firstName: 1, lastName: 1, weeklycommittedHours: 1, + timeOffFrom: 1, + timeOffTill: 1, } ) .then((res) => res) @@ -196,6 +204,8 @@ const taskHelper = function () { timeEntryByPerson[teamMember._id.toString()]?.totalSeconds / 3600 || 0, tasks: taskByPerson[teamMember._id.toString()] || [], + timeOffFrom: teamMember.timeOffFrom || null, + timeOffTill: teamMember.timeOffTill || null, }; teamMemberTasksData.push(obj); }); From 2f362910ce36b623aff3ddfe2ce02d2353153e74 Mon Sep 17 00:00:00 2001 From: Shiwani Rajagopalan Date: Mon, 8 Jan 2024 07:22:46 -0500 Subject: [PATCH 32/36] resolved errors after merge conflicts --- src/controllers/reasonSchedulingController.js | 11 +--------- src/helpers/dashboardhelper.js | 21 ++++++++++--------- src/helpers/taskHelper.js | 15 ------------- 3 files changed, 12 insertions(+), 35 deletions(-) diff --git a/src/controllers/reasonSchedulingController.js b/src/controllers/reasonSchedulingController.js index 4fce71b3a..a76dcf4a2 100644 --- a/src/controllers/reasonSchedulingController.js +++ b/src/controllers/reasonSchedulingController.js @@ -11,9 +11,6 @@ const postReason = async (req, res) => { .tz(reasonData.date, "America/Los_Angeles") .startOf("day"); const currentDate = moment.tz("America/Los_Angeles").startOf("day"); - .tz(reasonData.date, "America/Los_Angeles") - .startOf("day"); - const currentDate = moment.tz("America/Los_Angeles").startOf("day"); // error case 0 if (moment.tz(reasonData.date, "America/Los_Angeles").day() !== 0) { @@ -38,7 +35,6 @@ const postReason = async (req, res) => { }); } - // Commented this condition to make reason scheduler available to all the users. // error case 1 // if (requestor.role !== 'Owner' && requestor.role !== 'Administrator') { @@ -160,7 +156,6 @@ const postReason = async (req, res) => { // emailSender(`${foundUser.email},@gmail.com`, subject, emailBody, null, null); } - return res.sendStatus(200); } catch (error) { console.log(error); @@ -307,11 +302,9 @@ const patchReason = async (req, res) => { const savedData = await foundReason.save(); if (savedData) { - // Upon clicking the "Save" button in the Blue Square Reason Scheduler, an email will be automatically sent to the user and Jae. const subject = `Blue Square Reason for ${foundUser.firstName} ${foundUser.lastName} has been updated`; - const emailBody = `

Hi !

const emailBody = `

Hi !

This email is to let you know that ${foundUser.firstName} ${foundUser.lastName} has updated their Blue Square Reason.

@@ -331,7 +324,6 @@ const patchReason = async (req, res) => { // emailSender(`${foundUser.email},@gmail.com`, subject, emailBody, null, null); } - return res.status(200).json({ message: "Reason Updated!", message: "Reason Updated!", @@ -348,13 +340,12 @@ const deleteReason = async (req, res) => { const { reasonData, requestor } = req.body; const { userId } = req.params; - // error case 1 if (requestor.role !== "Owner" && requestor.role !== "Administrator") { return res.status(403).json({ message: "You must be an Owner or Administrator to schedule a reason for a Blue Square", - "You must be an Owner or Administrator to schedule a reason for a Blue Square", + errorCode: 1, }); } diff --git a/src/helpers/dashboardhelper.js b/src/helpers/dashboardhelper.js index 2babef0b2..9f172dd2a 100644 --- a/src/helpers/dashboardhelper.js +++ b/src/helpers/dashboardhelper.js @@ -174,20 +174,17 @@ const dashboardhelper = function () { .then((res) => res) .catch((e) => {}); - if (userById == null) return null; const userRole = userById.role; const pdtstart = moment() .tz("America/Los_Angeles") .startOf("week") .format("YYYY-MM-DD"); + + const pdtend = moment() .tz("America/Los_Angeles") - .startOf("week") + .endOf("week") .format("YYYY-MM-DD"); - const pdtend = moment() - .tz('America/Los_Angeles') - .endOf('week') - .format('YYYY-MM-DD'); let teamMemberIds = [userid]; let teamMembers = []; @@ -210,9 +207,6 @@ const dashboardhelper = function () { }); }); - teamMembers = await userProfile - .find( - { _id: { $in: teamMemberIds }, isActive: true }, teamMembers = await userProfile .find( { _id: { $in: teamMemberIds }, isActive: true }, @@ -235,6 +229,7 @@ const dashboardhelper = function () { teamMembers = await userProfile .find( { isActive: true, role: { $nin: excludedRoles } }, + { role: 1, firstName: 1, lastName: 1, @@ -300,7 +295,13 @@ const dashboardhelper = function () { timeEntries.map((timeEntry) => { const personIdStr = timeEntry.personId.toString(); - if (timeEntryByPerson[personIdStr] == null) { timeEntryByPerson[personIdStr] = { tangibleSeconds: 0, intangibleSeconds: 0, totalSeconds: 0 }; } + if (timeEntryByPerson[personIdStr] == null) { + timeEntryByPerson[personIdStr] = { + tangibleSeconds: 0, + intangibleSeconds: 0, + totalSeconds: 0, + }; + } if (timeEntry.isTangible === true) { timeEntryByPerson[personIdStr].tangibleSeconds += diff --git a/src/helpers/taskHelper.js b/src/helpers/taskHelper.js index 0d26b96bb..372c960dd 100644 --- a/src/helpers/taskHelper.js +++ b/src/helpers/taskHelper.js @@ -40,25 +40,10 @@ const taskHelper = function () { .tz("America/Los_Angeles") .endOf("week") .format("YYYY-MM-DD"); - .tz("America/Los_Angeles") - .endOf("week") - .format("YYYY-MM-DD"); let teamMemberIds = [userid]; let teamMembers = []; - let teamMemberIds = [userid]; - let teamMembers = []; - if ( - userRole != "Administrator" && - userRole != "Owner" && - userRole != "Core Team" - ) { - // Manager , Mentor , Volunteer ... , Show only team members - const teamsResult = await team - .find({ "members.userId": { $in: [userid] } }, { members: 1 }) - .then((res) => res) - .catch((e) => {}); if ( userRole != "Administrator" && userRole != "Owner" && From bd9a1500299a597190de07ff4eb38e6ef4b2ef77 Mon Sep 17 00:00:00 2001 From: Shiwani Rajagopalan Date: Mon, 8 Jan 2024 07:26:38 -0500 Subject: [PATCH 33/36] resolved errors in userProfile.js after conflicts --- src/models/userProfile.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/models/userProfile.js b/src/models/userProfile.js index 8f5e23d2c..aaa3923f5 100644 --- a/src/models/userProfile.js +++ b/src/models/userProfile.js @@ -1,13 +1,9 @@ const mongoose = require("mongoose"); const moment = require("moment-timezone"); -const mongoose = require("mongoose"); -const moment = require("moment-timezone"); const { Schema } = mongoose; const validate = require("mongoose-validator"); const bcrypt = require("bcryptjs"); -const validate = require("mongoose-validator"); -const bcrypt = require("bcryptjs"); const SALT_ROUNDS = 10; const nextDay = new Date(); From b03f597e8d3cb2bf59adbaad42d2f15262a6c81f Mon Sep 17 00:00:00 2001 From: Vishala Ramasamy Date: Wed, 10 Jan 2024 16:23:36 -0800 Subject: [PATCH 34/36] Lb view hier. for Admins --- src/helpers/dashboardhelper.js | 40 +--------------------------------- 1 file changed, 1 insertion(+), 39 deletions(-) diff --git a/src/helpers/dashboardhelper.js b/src/helpers/dashboardhelper.js index 9f172dd2a..8df4f6c9d 100644 --- a/src/helpers/dashboardhelper.js +++ b/src/helpers/dashboardhelper.js @@ -223,46 +223,8 @@ const dashboardhelper = function () { ) .then((res) => res) .catch((e) => {}); - } else if (userRole == "Administrator") { - // All users except Owner and Core Team - const excludedRoles = ["Core Team", "Owner"]; - teamMembers = await userProfile - .find( - { isActive: true, role: { $nin: excludedRoles } }, - { - role: 1, - firstName: 1, - lastName: 1, - isVisible: 1, - weeklycommittedHours: 1, - weeklySummaries: 1, - timeOffFrom: 1, - timeOffTill: 1, - } - ) - .then((res) => res) - .catch((e) => {}); - } else if (userRole == "Administrator") { - // All users except Owner and Core Team - const excludedRoles = ["Core Team", "Owner"]; - teamMembers = await userProfile - .find( - { isActive: true, role: { $nin: excludedRoles } }, - { - role: 1, - firstName: 1, - lastName: 1, - isVisible: 1, - weeklycommittedHours: 1, - weeklySummaries: 1, - timeOffFrom: 1, - timeOffTill: 1, - } - ) - .then((res) => res) - .catch((e) => {}); } else { - // 'Core Team', 'Owner' //All users + // 'Core Team', 'Owner' , 'Admin' //Show All users teamMembers = await userProfile .find( { isActive: true }, From 22401e614bdd41c2debce5511f44b0b944d5f5f0 Mon Sep 17 00:00:00 2001 From: wang9hu Date: Thu, 11 Jan 2024 22:07:52 -0800 Subject: [PATCH 35/36] refactor time entry handlers, fix bugs --- src/controllers/taskController.js | 14 +- src/controllers/timeEntryController.js | 92 ++-- src/controllers/userProfileController.js | 151 ++++--- src/helpers/dashboardhelper.js | 538 ++++++++++------------- src/helpers/taskHelper.js | 517 +++++++++++----------- src/helpers/timeEntryHelper.js | 6 +- src/models/timeentry.js | 6 +- 7 files changed, 632 insertions(+), 692 deletions(-) diff --git a/src/controllers/taskController.js b/src/controllers/taskController.js index a91a2b90d..423d45037 100644 --- a/src/controllers/taskController.js +++ b/src/controllers/taskController.js @@ -803,7 +803,7 @@ const taskController = function (Task) { return res.status(400).send({ error: 'This is not a valid task' }); } - const hoursLogged = await timeEntryHelper.getAllHoursLoggedForSpecifiedProject(taskId); + const hoursLogged = await timeEntryHelper.getAllHoursLoggedForSpecifiedTask(taskId); task.set('hoursLogged', hoursLogged, { strict: false }); // Fetch the resource names for all resources @@ -815,12 +815,10 @@ const taskController = function (Task) { resource.name = resourceNames[index] !== ' ' ? resourceNames[index] : resource.name; }); - res.status(200).send(task); + return res.status(200).send(task); } catch (error) { // Generic error message, you can adjust as needed - res - .status(500) - .send({ error: 'Internal Server Error', details: error.message }); + return res.status(500).send({ error: 'Internal Server Error', details: error.message }); } }; @@ -875,9 +873,9 @@ const taskController = function (Task) { }; const getTasksForTeamsByUser = async (req, res) => { + const userId = mongoose.Types.ObjectId(req.params.userId); try { - const userId = mongoose.Types.ObjectId(req.params.userId); - const teamsData = await taskHelper.getTasksForTeams(userId); + const teamsData = await taskHelper.getTasksForTeams(userId, req.body.requestor); if (teamsData.length > 0) { res.status(200).send(teamsData); } else { @@ -888,7 +886,7 @@ const taskController = function (Task) { } } catch (error) { console.log(error); - res.status(400).send(error); + res.status(400).send({ error }); } }; diff --git a/src/controllers/timeEntryController.js b/src/controllers/timeEntryController.js index 1cbab61c4..597a9f3eb 100644 --- a/src/controllers/timeEntryController.js +++ b/src/controllers/timeEntryController.js @@ -162,11 +162,6 @@ const timeEntrycontroller = function (TimeEntry) { return res.status(400).send({ error }); } - if (!mongoose.Types.ObjectId.isValid(timeEntryId)) { - const error = 'ObjectIds are not correctly formed'; - return res.status(400).send({ error }); - } - const { personId, hours: newHours = '00', @@ -179,6 +174,18 @@ const timeEntrycontroller = function (TimeEntry) { dateOfWork: newDateOfWork, } = req.body; + const type = req.body.entryType; + const isGeneralEntry = isGeneralTimeEntry(type); + + if ( + !mongoose.Types.ObjectId.isValid(timeEntryId) + || ((isGeneralEntry || type === 'project') + && !mongoose.Types.ObjectId.isValid(newProjectId)) + ) { + const error = 'ObjectIds are not correctly formed'; + return res.status(400).send({ error }); + } + const isForAuthUser = personId === req.body.requestor.requestorId; const isSameDayTimeEntry = moment().tz('America/Los_Angeles').format('YYYY-MM-DD') === newDateOfWork; const canEdit = (await hasPermission(req.body.requestor, 'editTimeEntry')) || (isForAuthUser && isSameDayTimeEntry); @@ -191,24 +198,7 @@ const timeEntrycontroller = function (TimeEntry) { const session = await mongoose.startSession(); session.startTransaction(); - const type = req.body.entryType; - const isGeneralEntry = isGeneralTimeEntry(type); - try { - if (!timeEntryId) { - const error = 'ObjectId in request param is not in correct format'; - return res.status(400).send({ error }); - } - - if ( - !mongoose.Types.ObjectId.isValid(timeEntryId) - || ((isGeneralEntry || type === 'project') - && !mongoose.Types.ObjectId.isValid(newProjectId) - )) { - const error = 'ObjectIds are not correctly formed'; - return res.status(400).send({ error }); - } - // Get initial timeEntry by timeEntryId const timeEntry = await TimeEntry.findById(timeEntryId); if (!timeEntry) { @@ -338,7 +328,7 @@ const timeEntrycontroller = function (TimeEntry) { timeEntry.dateOfWork = moment(newDateOfWork).format('YYYY-MM-DD'); await timeEntry.save(); - return res.status(200).send({ message: 'Successfully updated time entry' }); + return res.status(200).send(timeEntry); } catch (err) { await session.abortTransaction(); return res.status(400).send({ error: err.toString() }); @@ -509,30 +499,40 @@ const timeEntrycontroller = function (TimeEntry) { }, ' -createdDateTime', ) - .populate('projectId') - .sort({ lastModifiedDateTime: -1 }) - .then((results) => { - const data = []; - results.forEach((element) => { - const record = {}; - - record._id = element._id; - record.notes = element.notes; - record.isTangible = element.isTangible; - record.personId = element.personId; - record.projectId = element.projectId ? element.projectId._id : ''; - record.projectName = element.projectId - ? element.projectId.projectName - : ''; - record.dateOfWork = element.dateOfWork; - [record.hours, record.minutes] = formatSeconds(element.totalSeconds); - data.push(record); - }); - res.status(200).send(data); - }) - .catch((error) => { - res.status(400).send(error); + .populate('personId') + .populate('projectId') + .populate('taskId') + .populate('wbsId') + .sort({ lastModifiedDateTime: -1 }) + .then((results) => { + const data = []; + + results.forEach((element) => { + const record = {}; + record._id = element._id; + record.notes = element.notes; + record.isTangible = element.isTangible; + record.personId = element.personId._id; + record.userProfile = element.personId; + record.dateOfWork = element.dateOfWork; + [record.hours, record.minutes] = formatSeconds(element.totalSeconds); + record.projectId = element.projectId._id; + record.projectName = element.projectId.projectName; + record.projectCategory = element.projectId.category.toLowerCase(); + record.taskId = element.taskId?._id || null; + record.taskName = element.taskId?.taskName || null; + record.taskClassification = element.taskId?.classification?.toLowerCase() || null; + record.wbsId = element.wbsId?._id || null; + record.wbsName = element.wbsId?.wbsName || null; + + data.push(record); }); + + res.status(200).send(data); + }) + .catch((error) => { + res.status(400).send(error); + }); }; const getTimeEntriesForSpecifiedProject = function (req, res) { diff --git a/src/controllers/userProfileController.js b/src/controllers/userProfileController.js index 1a3dd504f..77c86d689 100644 --- a/src/controllers/userProfileController.js +++ b/src/controllers/userProfileController.js @@ -9,7 +9,6 @@ const userHelper = require('../helpers/userHelper')(); const TimeEntry = require('../models/timeentry'); const logger = require('../startup/logger'); const Badge = require('../models/badge'); -const userProfile = require('../models/userProfile'); const yearMonthDayDateValidator = require('../utilities/yearMonthDayDateValidator'); const cache = require('../utilities/nodeCache')(); const { hasPermission, canRequestorUpdateUser } = require('../utilities/permissions'); @@ -240,12 +239,17 @@ const userProfileController = function (UserProfile) { ) ); + if (!isRequestorAuthorized) { + res.status(403).send('You are not authorized to update this user'); + return; + } + const canEditTeamCode = req.body.requestor.role === 'Owner' || req.body.requestor.role === 'Administrator' || req.body.requestor.permissions?.frontPermissions.includes('editTeamCode'); - if (!isRequestorAuthorized) { - res.status(403).send('You are not authorized to update this user'); + if (!canEditTeamCode) { + res.status(403).send('You are not authorized to edit team code.'); return; } @@ -274,40 +278,40 @@ const userProfileController = function (UserProfile) { const originalinfringements = record.infringements ? record.infringements : []; - record.jobTitle = req.body.jobTitle; - record.emailPubliclyAccessible = req.body.emailPubliclyAccessible; - record.phoneNumberPubliclyAccessible = req.body.phoneNumberPubliclyAccessible; - - record.profilePic = req.body.profilePic; - record.firstName = req.body.firstName; - record.lastName = req.body.lastName; - record.jobTitle = req.body.jobTitle; - record.phoneNumber = req.body.phoneNumber; - record.bio = req.body.bio; - record.personalLinks = req.body.personalLinks; - record.lastModifiedDate = Date.now(); - record.location = req.body.location; - record.profilePic = req.body.profilePic; - record.privacySettings = req.body.privacySettings; - record.weeklySummaries = req.body.weeklySummaries; - record.weeklySummariesCount = req.body.weeklySummariesCount; - record.mediaUrl = req.body.mediaUrl; - record.timeZone = req.body.timeZone; - record.hoursByCategory = req.body.hoursByCategory; - record.totalTangibleHrs = req.body.totalTangibleHrs; - record.isVisible = req.body.isVisible || false; - record.isRehireable = req.body.isRehireable || false; - record.totalIntangibleHrs = req.body.totalIntangibleHrs; - record.bioPosted = req.body.bioPosted || 'default'; - record.isFirstTimelog = req.body.isFirstTimelog; - record.teamCode = req.body.teamCode; - - if (!canEditTeamCode && record.teamCode !== req.body.teamCode) { - res.status(403).send('You are not authorized to edit team code.'); - return; - } - record.teamCode = req.body.teamCode; + const commonFields = [ + 'jobTitle', + 'emailPubliclyAccessible', + 'phoneNumberPubliclyAccessible', + 'profilePic', + 'firstName', + 'lastName', + 'jobTitle', + 'phoneNumber', + 'bio', + 'personalLinks', + 'location', + 'profilePic', + 'privacySettings', + 'weeklySummaries', + 'weeklySummariesCount', + 'mediaUrl', + 'timeZone', + 'hoursByCategory', + 'totalTangibleHrs', + 'totalIntangibleHrs', + 'isFirstTimelog', + 'teamCode', + 'isVisible', + 'isRehireable', + 'bioPosted', + ]; + + commonFields.forEach((fieldName) => { + if (req.body[fieldName] !== undefined) record[fieldName] = req.body[fieldName]; + }); + + record.lastModifiedDate = Date.now(); // find userData in cache const isUserInCache = cache.hasCache('allusers'); @@ -320,12 +324,34 @@ const userProfileController = function (UserProfile) { userData = allUserData[userIdx]; } if (await hasPermission(req.body.requestor, 'putUserProfileImportantInfo')) { - record.role = req.body.role; - record.isRehireable = req.body.isRehireable; - record.isActive = req.body.isActive; + const importantFields = [ + 'role', + 'isRehireable', + 'isActive', + 'adminLinks', + 'isActive', + 'weeklySummaries', + 'weeklySummariesCount', + 'mediaUrl', + 'collaborationPreference', + 'weeklySummaryNotReq', + 'weeklySummaryOption', + 'categoryTangibleHrs', + 'totalTangibleHrs', + 'timeEntryEditHistory', + ]; + + importantFields.forEach((fieldName) => { + if (req.body[fieldName] !== undefined) record[fieldName] = req.body[fieldName]; + }); + + if (req.body.missedHours !== undefined) record.missedHours = req.body.role === 'Core Team' ? req.body?.missedHours ?? 0 : 0; + if (req.body.teams !== undefined) record.teams = Array.from(new Set(req.body.teams)); + if (req.body.projects !== undefined) record.projects = Array.from(new Set(req.body.projects)); + if (req.body.email !== undefined) record.email = req.body.email.toLowerCase(); // Logic to update weeklycommittedHours and the history of the committed hours made - if (record.weeklycommittedHours !== req.body.weeklycommittedHours) { + if (req.body.weeklycommittedHours !== undefined && record.weeklycommittedHours !== req.body.weeklycommittedHours) { record.weeklycommittedHours = req.body.weeklycommittedHours; // If their last update was made today, remove that @@ -348,28 +374,7 @@ const userProfileController = function (UserProfile) { record.weeklycommittedHoursHistory.push(newEntry); } - record.missedHours = req.body.role === 'Core Team' ? req.body?.missedHours ?? 0 : 0; - record.adminLinks = req.body.adminLinks; - record.teams = Array.from(new Set(req.body.teams)); - record.projects = Array.from(new Set(req.body.projects)); - record.isActive = req.body.isActive; - record.email = req.body.email.toLowerCase(); - record.weeklySummaries = req.body.weeklySummaries; - record.weeklySummariesCount = req.body.weeklySummariesCount; - record.mediaUrl = req.body.mediaUrl; - record.collaborationPreference = req.body.collaborationPreference; - record.weeklySummaryNotReq = req.body.weeklySummaryNotReq - ? req.body.weeklySummaryNotReq - : record.weeklySummaryNotReq; - record.weeklySummaryOption = req.body.weeklySummaryOption; - record.categoryTangibleHrs = req.body.categoryTangibleHrs - ? req.body.categoryTangibleHrs - : record.categoryTangibleHrs; - record.totalTangibleHrs = req.body.totalTangibleHrs; - record.timeEntryEditHistory = req.body.timeEntryEditHistory; - record.createdDate = moment(req.body.createdDate).toDate(); - - if (record.createdDate !== req.body.createdDate) { + if (req.body.createdDate !== undefined && record.createdDate !== req.body.createdDate) { record.createdDate = moment(req.body.createdDate).toDate(); // Make sure weeklycommittedHoursHistory isn't empty if (record.weeklycommittedHoursHistory.length === 0) { @@ -383,20 +388,21 @@ const userProfileController = function (UserProfile) { record.weeklycommittedHoursHistory[0].dateChanged = record.createdDate; } - record.bioPosted = req.body.bioPosted || 'default'; - - if (await hasPermission(req.body.requestor, 'putUserProfilePermissions')) { + if (req.body.permissions !== undefined && (await hasPermission(req.body.requestor, 'putUserProfilePermissions'))) { record.permissions = req.body.permissions; } - if (yearMonthDayDateValidator(req.body.endDate)) { - record.endDate = moment(req.body.endDate).toDate(); - if (isUserInCache) { - userData.endDate = record.endDate.toISOString(); + if (req.body.endDate !== undefined) { + if (yearMonthDayDateValidator(req.body.endDate)) { + record.endDate = moment(req.body.endDate).toDate(); + if (isUserInCache) { + userData.endDate = record.endDate.toISOString(); + } + } else { + record.set('endDate', undefined, { strict: false }); } - } else { - record.set('endDate', undefined, { strict: false }); } + if (isUserInCache) { userData.role = record.role; userData.weeklycommittedHours = record.weeklycommittedHours; @@ -405,7 +411,7 @@ const userProfileController = function (UserProfile) { userData.createdDate = record.createdDate.toISOString(); } } - if (await hasPermission(req.body.requestor, 'infringementAuthorizer')) { + if (req.body.infringements !== undefined && (await hasPermission(req.body.requestor, 'infringementAuthorizer'))) { record.infringements = req.body.infringements; } @@ -588,7 +594,6 @@ const userProfileController = function (UserProfile) { // remove user from cache, it should be loaded next time cache.removeCache(`user-${userId}`); if (!key || value === undefined) return res.status(400).send({ error: 'Missing property or value' }); - if (!key || value === undefined) return res.status(400).send({ error: 'Missing property or value' }); return UserProfile.findById(userId) .then((user) => { diff --git a/src/helpers/dashboardhelper.js b/src/helpers/dashboardhelper.js index 9f172dd2a..89cecbab9 100644 --- a/src/helpers/dashboardhelper.js +++ b/src/helpers/dashboardhelper.js @@ -1,27 +1,27 @@ -const moment = require("moment-timezone"); -const mongoose = require("mongoose"); -const userProfile = require("../models/userProfile"); -const timeentry = require("../models/timeentry"); -const myTeam = require("../helpers/helperModels/myTeam"); -const team = require("../models/team"); +const moment = require('moment-timezone'); +const mongoose = require('mongoose'); +const userProfile = require('../models/userProfile'); +const timeentry = require('../models/timeentry'); +const myTeam = require('../helpers/helperModels/myTeam'); +const team = require('../models/team'); const dashboardhelper = function () { const personaldetails = function (userId) { return userProfile.findById( userId, - "_id firstName lastName role profilePic badgeCollection" + '_id firstName lastName role profilePic badgeCollection', ); }; const getOrgData = async function () { const pdtstart = moment() - .tz("America/Los_Angeles") - .startOf("week") - .format("YYYY-MM-DD"); + .tz('America/Los_Angeles') + .startOf('week') + .format('YYYY-MM-DD'); const pdtend = moment() - .tz("America/Los_Angeles") - .endOf("week") - .format("YYYY-MM-DD"); + .tz('America/Los_Angeles') + .endOf('week') + .format('YYYY-MM-DD'); /** * Previous aggregate pipeline had two issues: @@ -40,42 +40,42 @@ const dashboardhelper = function () { $gte: 1, }, role: { - $ne: "Mentor", + $ne: 'Mentor', }, }, }, { $lookup: { - from: "timeEntries", - localField: "_id", - foreignField: "personId", - as: "timeEntryData", + from: 'timeEntries', + localField: '_id', + foreignField: 'personId', + as: 'timeEntryData', }, }, { $project: { - personId: "$_id", + personId: '$_id', name: 1, weeklycommittedHours: 1, role: 1, timeEntryData: { $filter: { - input: "$timeEntryData", - as: "timeentry", + input: '$timeEntryData', + as: 'timeentry', cond: { $and: [ { - $gte: ["$$timeentry.dateOfWork", pdtstart], + $gte: ['$$timeentry.dateOfWork', pdtstart], }, { - $lte: ["$$timeentry.dateOfWork", pdtend], + $lte: ['$$timeentry.dateOfWork', pdtend], }, { $not: [ { $in: [ - "$$timeentry.entryType", - ["person", "team", "project"], + '$$timeentry.entryType', + ['person', 'team', 'project'], ], }, ], @@ -88,7 +88,7 @@ const dashboardhelper = function () { }, { $unwind: { - path: "$timeEntryData", + path: '$timeEntryData', preserveNullAndEmptyArrays: true, }, }, @@ -99,27 +99,27 @@ const dashboardhelper = function () { totalSeconds: { $cond: [ { - $gte: ["$timeEntryData.totalSeconds", 0], + $gte: ['$timeEntryData.totalSeconds', 0], }, - "$timeEntryData.totalSeconds", + '$timeEntryData.totalSeconds', 0, ], }, tangibletime: { $cond: [ { - $eq: ["$timeEntryData.isTangible", true], + $eq: ['$timeEntryData.isTangible', true], }, - "$timeEntryData.totalSeconds", + '$timeEntryData.totalSeconds', 0, ], }, intangibletime: { $cond: [ { - $eq: ["$timeEntryData.isTangible", false], + $eq: ['$timeEntryData.isTangible', false], }, - "$timeEntryData.totalSeconds", + '$timeEntryData.totalSeconds', 0, ], }, @@ -128,17 +128,17 @@ const dashboardhelper = function () { { $group: { _id: { - personId: "$personId", - weeklycommittedHours: "$weeklycommittedHours", + personId: '$personId', + weeklycommittedHours: '$weeklycommittedHours', }, time_hrs: { - $sum: { $divide: ["$totalSeconds", 3600] }, + $sum: { $divide: ['$totalSeconds', 3600] }, }, tangibletime_hrs: { - $sum: { $divide: ["$tangibletime", 3600] }, + $sum: { $divide: ['$tangibletime', 3600] }, }, intangibletime_hrs: { - $sum: { $divide: ["$intangibletime", 3600] }, + $sum: { $divide: ['$intangibletime', 3600] }, }, }, }, @@ -146,19 +146,15 @@ const dashboardhelper = function () { $group: { _id: 0, memberCount: { $sum: 1 }, - totalweeklycommittedHours: { $sum: "$_id.weeklycommittedHours" }, - totalweeklycommittedHours: { $sum: "$_id.weeklycommittedHours" }, + totalweeklycommittedHours: { $sum: '$_id.weeklycommittedHours' }, totaltime_hrs: { - $sum: "$time_hrs", - $sum: "$time_hrs", + $sum: '$time_hrs', }, totaltangibletime_hrs: { - $sum: "$tangibletime_hrs", - $sum: "$tangibletime_hrs", + $sum: '$tangibletime_hrs', }, totalintangibletime_hrs: { - $sum: "$intangibletime_hrs", - $sum: "$intangibletime_hrs", + $sum: '$intangibletime_hrs', }, }, }, @@ -169,202 +165,154 @@ const dashboardhelper = function () { const getLeaderboard = async function (userId) { const userid = mongoose.Types.ObjectId(userId); - const userById = await userProfile - .findOne({ _id: userid, isActive: true }, { role: 1 }) - .then((res) => res) - .catch((e) => {}); + try { + const userById = await userProfile + .findOne({ _id: userid, isActive: true }, { role: 1 }); + + if (userById == null) return null; + const userRole = userById.role; + const pdtstart = moment() + .tz('America/Los_Angeles') + .startOf('week') + .format('YYYY-MM-DD'); + + const pdtend = moment() + .tz('America/Los_Angeles') + .endOf('week') + .format('YYYY-MM-DD'); + + let teamMemberIds = [userid]; + let teamMembers = []; + + if ( + userRole !== 'Administrator' + && userRole !== 'Owner' + && userRole !== 'Core Team' + ) { + // Manager , Mentor , Volunteer ... , Show only team members + const teamsResult = await team + .find({ 'members.userId': { $in: [userid] } }, { members: 1 }); + + teamsResult.forEach((_myTeam) => { + _myTeam.members.forEach((teamMember) => { + if (!teamMember.userId.equals(userid)) teamMemberIds.push(teamMember.userId); + }); + }); - if (userById == null) return null; - const userRole = userById.role; - const pdtstart = moment() - .tz("America/Los_Angeles") - .startOf("week") - .format("YYYY-MM-DD"); + teamMembers = await userProfile + .find( + { _id: { $in: teamMemberIds }, isActive: true }, + { + role: 1, + firstName: 1, + lastName: 1, + isVisible: 1, + weeklycommittedHours: 1, + weeklySummaries: 1, + timeOffFrom: 1, + timeOffTill: 1, + }, + ); + } else { + // 'Core Team', 'Owner' //All users + teamMembers = await userProfile + .find( + { isActive: true }, + { + role: 1, + firstName: 1, + lastName: 1, + isVisible: 1, + weeklycommittedHours: 1, + weeklySummaries: 1, + timeOffFrom: 1, + timeOffTill: 1, + }, + ); + } - const pdtend = moment() - .tz("America/Los_Angeles") - .endOf("week") - .format("YYYY-MM-DD"); - - let teamMemberIds = [userid]; - let teamMembers = []; - - if ( - userRole != "Administrator" && - userRole != "Owner" && - userRole != "Core Team" - ) { - // Manager , Mentor , Volunteer ... , Show only team members - const teamsResult = await team - .find({ "members.userId": { $in: [userid] } }, { members: 1 }) - .then((res) => res) - .catch((e) => {}); - - teamsResult.map((_myTeam) => { - _myTeam.members.map((teamMember) => { - if (!teamMember.userId.equals(userid)) - teamMemberIds.push(teamMember.userId); - }); - }); + teamMemberIds = teamMembers.map(member => member._id); - teamMembers = await userProfile - .find( - { _id: { $in: teamMemberIds }, isActive: true }, - { - role: 1, - firstName: 1, - lastName: 1, - isVisible: 1, - weeklycommittedHours: 1, - weeklySummaries: 1, - timeOffFrom: 1, - timeOffTill: 1, - } - ) - .then((res) => res) - .catch((e) => {}); - } else if (userRole == "Administrator") { - // All users except Owner and Core Team - const excludedRoles = ["Core Team", "Owner"]; - teamMembers = await userProfile - .find( - { isActive: true, role: { $nin: excludedRoles } }, - { - role: 1, - firstName: 1, - lastName: 1, - isVisible: 1, - weeklycommittedHours: 1, - weeklySummaries: 1, - timeOffFrom: 1, - timeOffTill: 1, - } - ) - .then((res) => res) - .catch((e) => {}); - } else if (userRole == "Administrator") { - // All users except Owner and Core Team - const excludedRoles = ["Core Team", "Owner"]; - teamMembers = await userProfile - .find( - { isActive: true, role: { $nin: excludedRoles } }, - { - role: 1, - firstName: 1, - lastName: 1, - isVisible: 1, - weeklycommittedHours: 1, - weeklySummaries: 1, - timeOffFrom: 1, - timeOffTill: 1, - } - ) - .then((res) => res) - .catch((e) => {}); - } else { - // 'Core Team', 'Owner' //All users - teamMembers = await userProfile - .find( - { isActive: true }, - { - role: 1, - firstName: 1, - lastName: 1, - isVisible: 1, - weeklycommittedHours: 1, - weeklySummaries: 1, - timeOffFrom: 1, - timeOffTill: 1, - } - ) - .then((res) => res) - .catch((e) => {}); - } + const timeEntries = await timeentry.find({ + dateOfWork: { + $gte: pdtstart, + $lte: pdtend, + }, + personId: { $in: teamMemberIds }, + }); - teamMemberIds = teamMembers.map((member) => member._id); + const timeEntryByPerson = {}; + timeEntries.forEach((timeEntry) => { + const personIdStr = timeEntry.personId.toString(); + + if (timeEntryByPerson[personIdStr] == null) { + timeEntryByPerson[personIdStr] = { + tangibleSeconds: 0, + intangibleSeconds: 0, + totalSeconds: 0, + }; + } - const timeEntries = await timeentry.find({ - dateOfWork: { - $gte: pdtstart, - $lte: pdtend, - }, - personId: { $in: teamMemberIds }, - }); - - const timeEntryByPerson = {}; - timeEntries.map((timeEntry) => { - const personIdStr = timeEntry.personId.toString(); - - if (timeEntryByPerson[personIdStr] == null) { - timeEntryByPerson[personIdStr] = { - tangibleSeconds: 0, - intangibleSeconds: 0, - totalSeconds: 0, - }; - } + if (timeEntry.isTangible === true) { + timeEntryByPerson[personIdStr].tangibleSeconds += timeEntry.totalSeconds; + } else { + timeEntryByPerson[personIdStr].intangibleSeconds += timeEntry.totalSeconds; + } - if (timeEntry.isTangible === true) { - timeEntryByPerson[personIdStr].tangibleSeconds += - timeEntry.totalSeconds; - } else { - timeEntryByPerson[personIdStr].intangibleSeconds += - timeEntry.totalSeconds; - } + timeEntryByPerson[personIdStr].totalSeconds += timeEntry.totalSeconds; + }); - timeEntryByPerson[personIdStr].totalSeconds += timeEntry.totalSeconds; - }); - - const leaderBoardData = []; - teamMembers.map((teamMember) => { - const obj = { - personId: teamMember._id, - role: teamMember.role, - name: `${teamMember.firstName} ${teamMember.lastName}`, - isVisible: teamMember.isVisible, - hasSummary: - teamMember.weeklySummaries?.length > 0 - ? teamMember.weeklySummaries[0].summary != "" - : false, - weeklycommittedHours: teamMember.weeklycommittedHours, - totaltangibletime_hrs: - timeEntryByPerson[teamMember._id.toString()]?.tangibleSeconds / - 3600 || 0, - totalintangibletime_hrs: - timeEntryByPerson[teamMember._id.toString()]?.intangibleSeconds / - 3600 || 0, - totaltime_hrs: - timeEntryByPerson[teamMember._id.toString()]?.totalSeconds / 3600 || - 0, - percentagespentintangible: - timeEntryByPerson[teamMember._id.toString()] && - timeEntryByPerson[teamMember._id.toString()]?.totalSeconds != 0 && - timeEntryByPerson[teamMember._id.toString()]?.tangibleSeconds != 0 - ? (timeEntryByPerson[teamMember._id.toString()]?.tangibleSeconds / - timeEntryByPerson[teamMember._id.toString()]?.totalSeconds) * - 100 - : 0, - timeOffFrom: teamMember.timeOffFrom || null, - timeOffTill: teamMember.timeOffTill || null, - }; - leaderBoardData.push(obj); - }); - - const sortedLBData = leaderBoardData.sort((a, b) => { - // Sort by totaltangibletime_hrs in descending order - if (b.totaltangibletime_hrs !== a.totaltangibletime_hrs) { - return b.totaltangibletime_hrs - a.totaltangibletime_hrs; - } + const leaderBoardData = []; + teamMembers.forEach((teamMember) => { + const obj = { + personId: teamMember._id, + role: teamMember.role, + name: `${teamMember.firstName} ${teamMember.lastName}`, + isVisible: teamMember.isVisible, + hasSummary: + teamMember.weeklySummaries?.length > 0 + ? teamMember.weeklySummaries[0].summary !== '' + : false, + weeklycommittedHours: teamMember.weeklycommittedHours, + totaltangibletime_hrs: + timeEntryByPerson[teamMember._id.toString()]?.tangibleSeconds / 3600 || 0, + totalintangibletime_hrs: + timeEntryByPerson[teamMember._id.toString()]?.intangibleSeconds / 3600 || 0, + totaltime_hrs: + timeEntryByPerson[teamMember._id.toString()]?.totalSeconds / 3600 || 0, + percentagespentintangible: + timeEntryByPerson[teamMember._id.toString()] + && timeEntryByPerson[teamMember._id.toString()]?.totalSeconds !== 0 + && timeEntryByPerson[teamMember._id.toString()]?.tangibleSeconds !== 0 + ? (timeEntryByPerson[teamMember._id.toString()]?.tangibleSeconds + / timeEntryByPerson[teamMember._id.toString()]?.totalSeconds) + * 100 + : 0, + timeOffFrom: teamMember.timeOffFrom || null, + timeOffTill: teamMember.timeOffTill || null, + }; + leaderBoardData.push(obj); + }); - // Then sort by name in ascending order - if (a.name !== b.name) { - return a.name.localeCompare(b.name); - } + const sortedLBData = leaderBoardData.sort((a, b) => { + // Sort by totaltangibletime_hrs in descending order + if (b.totaltangibletime_hrs !== a.totaltangibletime_hrs) { + return b.totaltangibletime_hrs - a.totaltangibletime_hrs; + } - // Finally, sort by role in ascending order - return a.role.localeCompare(b.role); - }); + // Then sort by name in ascending order + if (a.name !== b.name) { + return a.name.localeCompare(b.name); + } - return sortedLBData; + // Finally, sort by role in ascending order + return a.role.localeCompare(b.role); + }); + return sortedLBData; + } catch (error) { + console.log(error); + return new Error(error); + } // return myTeam.aggregate([ // { @@ -624,14 +572,14 @@ const dashboardhelper = function () { const getUserLaborData = async function (userId) { try { const pdtStart = moment() - .tz("America/Los_Angeles") - .startOf("week") - .format("YYYY-MM-DD"); + .tz('America/Los_Angeles') + .startOf('week') + .format('YYYY-MM-DD'); const pdtEnd = moment() - .tz("America/Los_Angeles") - .endOf("week") - .format("YYYY-MM-DD"); + .tz('America/Los_Angeles') + .endOf('week') + .format('YYYY-MM-DD'); const user = await userProfile.findById({ _id: userId, @@ -642,7 +590,7 @@ const dashboardhelper = function () { $gte: pdtStart, $lte: pdtEnd, }, - entryType: { $in: ["default", null] }, + entryType: { $in: ['default', null] }, personId: userId, }); @@ -662,7 +610,7 @@ const dashboardhelper = function () { personId: userId, role: user.role, isVisible: user.isVisible, - hasSummary: user.weeklySummaries[0].summary !== "", + hasSummary: user.weeklySummaries[0].summary !== '', weeklycommittedHours: user.weeklycommittedHours, name: `${user.firstName} ${user.lastName}`, totaltime_hrs: (tangibleSeconds + intangibleSeconds) / 3600, @@ -677,8 +625,8 @@ const dashboardhelper = function () { } catch (err) { return [ { - personId: "error", - name: "Error Error", + personId: 'error', + name: 'Error Error', totaltime_hrs: 0, totaltangibletime_hrs: 0, totalintangibletime_hrs: 0, @@ -689,8 +637,8 @@ const dashboardhelper = function () { }; const laborthismonth = function (userId, startDate, endDate) { - const fromdate = moment(startDate).format("YYYY-MM-DD"); - const todate = moment(endDate).format("YYYY-MM-DD"); + const fromdate = moment(startDate).format('YYYY-MM-DD'); + const todate = moment(endDate).format('YYYY-MM-DD'); return timeentry.aggregate([ { @@ -706,19 +654,19 @@ const dashboardhelper = function () { { $group: { _id: { - projectId: "$projectId", + projectId: '$projectId', }, labor: { - $sum: "$totalSeconds", + $sum: '$totalSeconds', }, }, }, { $lookup: { - from: "projects", - localField: "_id.projectId", - foreignField: "_id", - as: "project", + from: 'projects', + localField: '_id.projectId', + foreignField: '_id', + as: 'project', }, }, { @@ -727,13 +675,13 @@ const dashboardhelper = function () { projectName: { $ifNull: [ { - $arrayElemAt: ["$project.projectName", 0], + $arrayElemAt: ['$project.projectName', 0], }, - "Undefined", + 'Undefined', ], }, timeSpent_hrs: { - $divide: ["$labor", 3600], + $divide: ['$labor', 3600], }, }, }, @@ -741,8 +689,8 @@ const dashboardhelper = function () { }; const laborthisweek = function (userId, startDate, endDate) { - const fromdate = moment(startDate).format("YYYY-MM-DD"); - const todate = moment(endDate).format("YYYY-MM-DD"); + const fromdate = moment(startDate).format('YYYY-MM-DD'); + const todate = moment(endDate).format('YYYY-MM-DD'); return userProfile.aggregate([ { @@ -758,10 +706,10 @@ const dashboardhelper = function () { }, { $lookup: { - from: "timeEntries", - localField: "_id", - foreignField: "personId", - as: "timeEntryData", + from: 'timeEntries', + localField: '_id', + foreignField: 'personId', + as: 'timeEntryData', }, }, { @@ -769,25 +717,25 @@ const dashboardhelper = function () { weeklycommittedHours: 1, timeEntryData: { $filter: { - input: "$timeEntryData", - as: "timeentry", + input: '$timeEntryData', + as: 'timeentry', cond: { $and: [ { - $eq: ["$$timeentry.isTangible", true], + $eq: ['$$timeentry.isTangible', true], }, { - $gte: ["$$timeentry.dateOfWork", fromdate], + $gte: ['$$timeentry.dateOfWork', fromdate], }, { - $lte: ["$$timeentry.dateOfWork", todate], + $lte: ['$$timeentry.dateOfWork', todate], }, { $not: [ { $in: [ - "$$timeentry.entryType", - ["person", "team", "project"], + '$$timeentry.entryType', + ['person', 'team', 'project'], ], }, ], @@ -800,27 +748,27 @@ const dashboardhelper = function () { }, { $unwind: { - path: "$timeEntryData", + path: '$timeEntryData', preserveNullAndEmptyArrays: true, }, }, { $group: { _id: { - _id: "$_id", - weeklycommittedHours: "$weeklycommittedHours", + _id: '$_id', + weeklycommittedHours: '$weeklycommittedHours', }, effort: { - $sum: "$timeEntryData.totalSeconds", + $sum: '$timeEntryData.totalSeconds', }, }, }, { $project: { _id: 0, - weeklycommittedHours: "$_id.weeklycommittedHours", + weeklycommittedHours: '$_id.weeklycommittedHours', timeSpent_hrs: { - $divide: ["$effort", 3600], + $divide: ['$effort', 3600], }, }, }, @@ -828,8 +776,8 @@ const dashboardhelper = function () { }; const laborThisWeekByCategory = function (userId, startDate, endDate) { - const fromdate = moment(startDate).format("YYYY-MM-DD"); - const todate = moment(endDate).format("YYYY-MM-DD"); + const fromdate = moment(startDate).format('YYYY-MM-DD'); + const todate = moment(endDate).format('YYYY-MM-DD'); return userProfile.aggregate([ { @@ -845,10 +793,10 @@ const dashboardhelper = function () { }, { $lookup: { - from: "timeEntries", - localField: "_id", - foreignField: "personId", - as: "timeEntryData", + from: 'timeEntries', + localField: '_id', + foreignField: 'personId', + as: 'timeEntryData', }, }, { @@ -856,25 +804,25 @@ const dashboardhelper = function () { weeklycommittedHours: 1, timeEntryData: { $filter: { - input: "$timeEntryData", - as: "timeentry", + input: '$timeEntryData', + as: 'timeentry', cond: { $and: [ { - $eq: ["$$timeentry.isTangible", true], + $eq: ['$$timeentry.isTangible', true], }, { - $gte: ["$$timeentry.dateOfWork", fromdate], + $gte: ['$$timeentry.dateOfWork', fromdate], }, { - $lte: ["$$timeentry.dateOfWork", todate], + $lte: ['$$timeentry.dateOfWork', todate], }, { $not: [ { $in: [ - "$$timeentry.entryType", - ["person", "team", "project"], + '$$timeentry.entryType', + ['person', 'team', 'project'], ], }, ], @@ -887,37 +835,37 @@ const dashboardhelper = function () { }, { $unwind: { - path: "$timeEntryData", + path: '$timeEntryData', preserveNullAndEmptyArrays: true, }, }, { $group: { - _id: "$timeEntryData.projectId", + _id: '$timeEntryData.projectId', effort: { - $sum: "$timeEntryData.totalSeconds", + $sum: '$timeEntryData.totalSeconds', }, }, }, { $lookup: { - from: "projects", - localField: "_id", - foreignField: "_id", - as: "project", + from: 'projects', + localField: '_id', + foreignField: '_id', + as: 'project', }, }, { $unwind: { - path: "$project", + path: '$project', preserveNullAndEmptyArrays: true, }, }, { $group: { - _id: "$project.category", + _id: '$project.category', effort: { - $sum: "$effort", + $sum: '$effort', }, }, }, @@ -925,7 +873,7 @@ const dashboardhelper = function () { $project: { _id: 1, timeSpent_hrs: { - $divide: ["$effort", 3600], + $divide: ['$effort', 3600], }, }, }, diff --git a/src/helpers/taskHelper.js b/src/helpers/taskHelper.js index 372c960dd..96058e6c3 100644 --- a/src/helpers/taskHelper.js +++ b/src/helpers/taskHelper.js @@ -1,222 +1,211 @@ -const moment = require("moment-timezone"); -const mongoose = require("mongoose"); -const userProfile = require("../models/userProfile"); -const timeentry = require("../models/timeentry"); -const myTeam = require("../helpers/helperModels/myTeam"); -const team = require("../models/team"); -const Task = require("../models/task"); -const TaskNotification = require("../models/taskNotification"); -const Wbs = require("../models/wbs"); +const moment = require('moment-timezone'); +const mongoose = require('mongoose'); +const userProfile = require('../models/userProfile'); +const timeentry = require('../models/timeentry'); +const team = require('../models/team'); +const Task = require('../models/task'); +const TaskNotification = require('../models/taskNotification'); const taskHelper = function () { - const getTasksForTeams = async function (userId) { + const getTasksForTeams = async function (userId, requestor) { const userid = mongoose.Types.ObjectId(userId); - const userById = await userProfile - .findOne( - { _id: userid, isActive: true }, - { - role: 1, - firstName: 1, - lastName: 1, - role: 1, - isVisible: 1, - weeklycommittedHours: 1, - weeklySummaries: 1, - timeOffFrom: 1, - timeOffTill: 1, - } - ) - .then((res) => res) - .catch((e) => {}); - - if (userById == null) return null; - const userRole = userById.role; - - const pdtstart = moment() - .tz("America/Los_Angeles") - .startOf("week") - .format("YYYY-MM-DD"); - const pdtend = moment() - .tz("America/Los_Angeles") - .endOf("week") - .format("YYYY-MM-DD"); - - let teamMemberIds = [userid]; - let teamMembers = []; - - if ( - userRole != "Administrator" && - userRole != "Owner" && - userRole != "Core Team" - ) { - // Manager , Mentor , Volunteer ... , Show only team members - const teamsResult = await team - .find({ "members.userId": { $in: [userid] } }, { members: 1 }) - .then((res) => res) - .catch((e) => {}); - - teamsResult.map((_myTeam) => { - _myTeam.members.map((teamMember) => { - if (!teamMember.userId.equals(userid)) - teamMemberIds.push(teamMember.userId); - }); - }); - teamsResult.map((_myTeam) => { - _myTeam.members.map((teamMember) => { - if (!teamMember.userId.equals(userid)) - teamMemberIds.push(teamMember.userId); - }); - }); - - teamMembers = await userProfile - .find( - { _id: { $in: teamMemberIds }, isActive: true }, + const requestorId = mongoose.Types.ObjectId(requestor.requestorId); + const requestorRole = requestor.role; + try { + const userById = await userProfile + .findOne( + { _id: userid, isActive: true }, { role: 1, firstName: 1, lastName: 1, + isVisible: 1, weeklycommittedHours: 1, + weeklySummaries: 1, timeOffFrom: 1, timeOffTill: 1, - } - ) - .then((res) => res) - .catch((e) => {}); - } else if (userRole == "Administrator") { - // All users except Owner and Core Team - const excludedRoles = ["Core Team", "Owner"]; - teamMembers = await userProfile - .find( - { isActive: true, role: { $nin: excludedRoles } }, - { - role: 1, - firstName: 1, - lastName: 1, - weeklycommittedHours: 1, - timeOffFrom: 1, - timeOffTill: 1, - } - ) - .then((res) => res) - .catch((e) => {}); - } else { - // 'Core Team', 'Owner' //All users - teamMembers = await userProfile - .find( - { isActive: true }, - { - role: 1, - firstName: 1, - lastName: 1, - weeklycommittedHours: 1, - timeOffFrom: 1, - timeOffTill: 1, - } - ) - .then((res) => res) - .catch((e) => {}); - } + }, + ); - teamMemberIds = teamMembers.map((member) => member._id); + if (userById === null) return null; + const userRole = userById.role; - const timeEntries = await timeentry.find({ - dateOfWork: { - $gte: pdtstart, - $lte: pdtend, - }, - personId: { $in: teamMemberIds }, - }); + const pdtstart = moment() + .tz('America/Los_Angeles') + .startOf('week') + .format('YYYY-MM-DD'); + const pdtend = moment() + .tz('America/Los_Angeles') + .endOf('week') + .format('YYYY-MM-DD'); - const timeEntryByPerson = {}; - timeEntries.map((timeEntry) => { - const personIdStr = timeEntry.personId.toString(); + let teamMemberIds = [userid]; + let teamMembers = []; - if (timeEntryByPerson[personIdStr] == null) { - timeEntryByPerson[personIdStr] = { - tangibleSeconds: 0, - intangibleSeconds: 0, - totalSeconds: 0, - }; - } + const isRequestorOwnerLike = ['Administrator', 'Owner', 'Core Team'].includes(requestorRole); + const isUserOwnerLike = ['Administrator', 'Owner', 'Core Team'].includes(userRole); - if (timeEntry.isTangible === true) { - timeEntryByPerson[personIdStr].tangibleSeconds += - timeEntry.totalSeconds; - } - timeEntryByPerson[personIdStr].totalSeconds += timeEntry.totalSeconds; - }); + switch (true) { + case isRequestorOwnerLike && isUserOwnerLike: { + teamMembers = await userProfile.find( + { isActive: true }, + { + role: 1, + firstName: 1, + lastName: 1, + weeklycommittedHours: 1, + timeOffFrom: 1, + timeOffTill: 1, + }, + ); + break; + } + case !isRequestorOwnerLike && !isUserOwnerLike: { + const sharedTeamsResult = await team.find({ $or: [{ 'members.userId': { $all: [userid, requestorId] } }, { 'members.userId': userid }] }, { members: 1 }); - const teamMemberTasks = await Task.find( - { "resources.userID": { $in: teamMemberIds } }, - { "resources.profilePic": 0 } - ).populate({ - path: "wbsId", - select: "projectId", - }); - const teamMemberTaskIds = teamMemberTasks.map((task) => task._id); - const teamMemberTaskNotifications = await TaskNotification.find({ - taskId: { $in: teamMemberTaskIds }, - }); + sharedTeamsResult.forEach((_myTeam) => { + _myTeam.members.forEach((teamMember) => { + if (!teamMember.userId.equals(userid)) teamMemberIds.push(teamMember.userId); + }); + }); + + teamMembers = await userProfile + .find( + { _id: { $in: teamMemberIds }, isActive: true }, + { + role: 1, + firstName: 1, + lastName: 1, + weeklycommittedHours: 1, + timeOffFrom: 1, + timeOffTill: 1, + }, + ); + break; + } + default: { + const teamsResult = await team.find({ 'members.userId': { $in: [userid] } }, { members: 1 }); - const taskNotificationByTaskNdUser = []; - teamMemberTaskNotifications.map((teamMemberTaskNotification) => { - const taskIdStr = teamMemberTaskNotification.taskId.toString(); - const userIdStr = teamMemberTaskNotification.userId.toString(); - const taskNdUserID = `${taskIdStr},${userIdStr}`; + teamsResult.forEach((_myTeam) => { + _myTeam.members.forEach((teamMember) => { + if (!teamMember.userId.equals(userid)) teamMemberIds.push(teamMember.userId); + }); + }); - if (taskNotificationByTaskNdUser[taskNdUserID]) { - taskNotificationByTaskNdUser[taskNdUserID].push( - teamMemberTaskNotification - ); - } else { - taskNotificationByTaskNdUser[taskNdUserID] = [ - teamMemberTaskNotification, - ]; + teamMembers = await userProfile + .find( + { _id: { $in: teamMemberIds }, isActive: true }, + { + role: 1, + firstName: 1, + lastName: 1, + weeklycommittedHours: 1, + timeOffFrom: 1, + timeOffTill: 1, + }, + ); + } } - }); - const taskByPerson = []; + teamMemberIds = teamMembers.map(member => member._id); + + const timeEntries = await timeentry.find({ + dateOfWork: { + $gte: pdtstart, + $lte: pdtend, + }, + personId: { $in: teamMemberIds }, + }); + + const timeEntryByPerson = {}; + timeEntries.forEach((timeEntry) => { + const personIdStr = timeEntry.personId.toString(); + if (!timeEntryByPerson[personIdStr]) { + timeEntryByPerson[personIdStr] = { + tangibleSeconds: 0, + intangibleSeconds: 0, + totalSeconds: 0, + }; + } + if (timeEntry.isTangible) { + timeEntryByPerson[personIdStr].tangibleSeconds += timeEntry.totalSeconds; + } + timeEntryByPerson[personIdStr].totalSeconds += timeEntry.totalSeconds; + }); + const teamMemberTasks = await Task.find( + { + 'resources.userID': { $in: teamMemberIds }, + }, + { + 'resources.profilePic': 0, + }, + ).populate({ + path: 'wbsId', + select: 'projectId', + }); + const teamMemberTaskIds = teamMemberTasks.map(task => task._id); + const teamMemberTaskNotifications = await TaskNotification.find({ taskId: { $in: teamMemberTaskIds } }); - teamMemberTasks.map((teamMemberTask) => { - const projId = teamMemberTask.wbsId?.projectId; - const _teamMemberTask = { ...teamMemberTask._doc }; - _teamMemberTask.projectId = projId; - const taskIdStr = _teamMemberTask._id.toString(); + const taskNotificationByTaskNdUser = []; + teamMemberTaskNotifications.forEach((teamMemberTaskNotification) => { + const taskIdStr = teamMemberTaskNotification.taskId.toString(); + const userIdStr = teamMemberTaskNotification.userId.toString(); + const taskNdUserID = `${taskIdStr},${userIdStr}`; - teamMemberTask.resources.map((resource) => { - const resourceIdStr = resource.userID.toString(); - const taskNdUserID = `${taskIdStr},${resourceIdStr}`; - _teamMemberTask.taskNotifications = - taskNotificationByTaskNdUser[taskNdUserID] || []; - if (taskByPerson[resourceIdStr]) { - taskByPerson[resourceIdStr].push(_teamMemberTask); + if (taskNotificationByTaskNdUser[taskNdUserID]) { + taskNotificationByTaskNdUser[taskNdUserID].push( + teamMemberTaskNotification, + ); } else { - taskByPerson[resourceIdStr] = [_teamMemberTask]; + taskNotificationByTaskNdUser[taskNdUserID] = [ + teamMemberTaskNotification, + ]; } }); - }); - const teamMemberTasksData = []; - teamMembers.map((teamMember) => { - const obj = { - personId: teamMember._id, - role: teamMember.role, - name: `${teamMember.firstName} ${teamMember.lastName}`, - weeklycommittedHours: teamMember.weeklycommittedHours, - totaltangibletime_hrs: - timeEntryByPerson[teamMember._id.toString()]?.tangibleSeconds / - 3600 || 0, - totaltime_hrs: - timeEntryByPerson[teamMember._id.toString()]?.totalSeconds / 3600 || - 0, - tasks: taskByPerson[teamMember._id.toString()] || [], - timeOffFrom: teamMember.timeOffFrom || null, - timeOffTill: teamMember.timeOffTill || null, - }; - teamMemberTasksData.push(obj); - }); + const taskByPerson = []; + + teamMemberTasks.forEach((teamMemberTask) => { + const projId = teamMemberTask.wbsId?.projectId; + const _teamMemberTask = { ...teamMemberTask._doc }; + _teamMemberTask.projectId = projId; + const taskIdStr = _teamMemberTask._id.toString(); + + teamMemberTask.resources.forEach((resource) => { + const resourceIdStr = resource.userID?.toString(); + const taskNdUserID = `${taskIdStr},${resourceIdStr}`; + _teamMemberTask.taskNotifications = taskNotificationByTaskNdUser[taskNdUserID] || []; + if (taskByPerson[resourceIdStr]) { + taskByPerson[resourceIdStr].push(_teamMemberTask); + } else { + taskByPerson[resourceIdStr] = [_teamMemberTask]; + } + }); + }); - return teamMemberTasksData; + const teamMemberTasksData = []; + teamMembers.forEach((teamMember) => { + const obj = { + personId: teamMember._id, + role: teamMember.role, + name: `${teamMember.firstName} ${teamMember.lastName}`, + weeklycommittedHours: teamMember.weeklycommittedHours, + totaltangibletime_hrs: + timeEntryByPerson[teamMember._id.toString()]?.tangibleSeconds / 3600 || 0, + totaltime_hrs: + timeEntryByPerson[teamMember._id.toString()]?.totalSeconds / 3600 || 0, + tasks: taskByPerson[teamMember._id.toString()] || [], + timeOffFrom: teamMember.timeOffFrom || null, + timeOffTill: teamMember.timeOffTill || null, + }; + teamMemberTasksData.push(obj); + }); + + return teamMemberTasksData; + } catch (error) { + console.log(error); + return new Error(error); + } // return myteam.aggregate([ // { @@ -503,13 +492,13 @@ const taskHelper = function () { }; const getTasksForSingleUser = function (userId) { const pdtstart = moment() - .tz("America/Los_Angeles") - .startOf("week") - .format("YYYY-MM-DD"); + .tz('America/Los_Angeles') + .startOf('week') + .format('YYYY-MM-DD'); const pdtend = moment() - .tz("America/Los_Angeles") - .endOf("week") - .format("YYYY-MM-DD"); + .tz('America/Los_Angeles') + .endOf('week') + .format('YYYY-MM-DD'); return userProfile.aggregate([ { $match: { @@ -518,33 +507,33 @@ const taskHelper = function () { }, { $project: { - personId: "$_id", - role: "$role", + personId: '$_id', + role: '$role', name: { - $concat: ["$firstName", " ", "$lastName"], + $concat: ['$firstName', ' ', '$lastName'], }, weeklycommittedHours: { $sum: [ - "$weeklycommittedHours", + '$weeklycommittedHours', { - $ifNull: ["$missedHours", 0], + $ifNull: ['$missedHours', 0], }, ], }, timeOffFrom: { - $ifNull: ["$timeOffFrom", null], + $ifNull: ['$timeOffFrom', null], }, timeOffTill: { - $ifNull: ["$timeOffTill", null], + $ifNull: ['$timeOffTill', null], }, }, }, { $lookup: { - from: "timeEntries", - localField: "personId", - foreignField: "personId", - as: "timeEntryData", + from: 'timeEntries', + localField: 'personId', + foreignField: 'personId', + as: 'timeEntryData', }, }, { @@ -557,18 +546,18 @@ const taskHelper = function () { role: 1, timeEntryData: { $filter: { - input: "$timeEntryData", - as: "timeentry", + input: '$timeEntryData', + as: 'timeentry', cond: { $and: [ { - $gte: ["$$timeentry.dateOfWork", pdtstart], + $gte: ['$$timeentry.dateOfWork', pdtstart], }, { - $lte: ["$$timeentry.dateOfWork", pdtend], + $lte: ['$$timeentry.dateOfWork', pdtend], }, { - $in: ["$$timeentry.entryType", ["default", null]], + $in: ['$$timeentry.entryType', ['default', null]], }, ], }, @@ -578,7 +567,7 @@ const taskHelper = function () { }, { $unwind: { - path: "$timeEntryData", + path: '$timeEntryData', preserveNullAndEmptyArrays: true, }, }, @@ -593,18 +582,18 @@ const taskHelper = function () { totalSeconds: { $cond: [ { - $gte: ["$timeEntryData.totalSeconds", 0], + $gte: ['$timeEntryData.totalSeconds', 0], }, - "$timeEntryData.totalSeconds", + '$timeEntryData.totalSeconds', 0, ], }, isTangible: { $cond: [ { - $gte: ["$timeEntryData.totalSeconds", 0], + $gte: ['$timeEntryData.totalSeconds', 0], }, - "$timeEntryData.isTangible", + '$timeEntryData.isTangible', false, ], }, @@ -615,9 +604,9 @@ const taskHelper = function () { tangibletime: { $cond: [ { - $eq: ["$isTangible", true], + $eq: ['$isTangible', true], }, - "$totalSeconds", + '$totalSeconds', 0, ], }, @@ -626,44 +615,44 @@ const taskHelper = function () { { $group: { _id: { - personId: "$personId", - weeklycommittedHours: "$weeklycommittedHours", - timeOffFrom: "$timeOffFrom", - timeOffTill: "$timeOffTill", - name: "$name", - role: "$role", + personId: '$personId', + weeklycommittedHours: '$weeklycommittedHours', + timeOffFrom: '$timeOffFrom', + timeOffTill: '$timeOffTill', + name: '$name', + role: '$role', }, totalSeconds: { - $sum: "$totalSeconds", + $sum: '$totalSeconds', }, tangibletime: { - $sum: "$tangibletime", + $sum: '$tangibletime', }, }, }, { $project: { _id: 0, - personId: "$_id.personId", - name: "$_id.name", - weeklycommittedHours: "$_id.weeklycommittedHours", - timeOffFrom: "$_id.timeOffFrom", - timeOffTill: "$_id.timeOffTill", - role: "$_id.role", + personId: '$_id.personId', + name: '$_id.name', + weeklycommittedHours: '$_id.weeklycommittedHours', + timeOffFrom: '$_id.timeOffFrom', + timeOffTill: '$_id.timeOffTill', + role: '$_id.role', totaltime_hrs: { - $divide: ["$totalSeconds", 3600], + $divide: ['$totalSeconds', 3600], }, totaltangibletime_hrs: { - $divide: ["$tangibletime", 3600], + $divide: ['$tangibletime', 3600], }, }, }, { $lookup: { - from: "tasks", - localField: "personId", - foreignField: "resources.userID", - as: "tasks", + from: 'tasks', + localField: 'personId', + foreignField: 'resources.userID', + as: 'tasks', }, }, { @@ -677,25 +666,25 @@ const taskHelper = function () { }, { $unwind: { - path: "$tasks", + path: '$tasks', preserveNullAndEmptyArrays: true, }, }, { $lookup: { - from: "wbs", - localField: "tasks.wbsId", - foreignField: "_id", - as: "projectId", + from: 'wbs', + localField: 'tasks.wbsId', + foreignField: '_id', + as: 'projectId', }, }, { $addFields: { - "tasks.projectId": { + 'tasks.projectId': { $cond: [ - { $ne: ["$projectId", []] }, - { $arrayElemAt: ["$projectId", 0] }, - "$tasks.projectId", + { $ne: ['$projectId', []] }, + { $arrayElemAt: ['$projectId', 0] }, + '$tasks.projectId', ], }, }, @@ -717,40 +706,40 @@ const taskHelper = function () { }, { $addFields: { - "tasks.projectId": "$tasks.projectId.projectId", + 'tasks.projectId': '$tasks.projectId.projectId', }, }, { $lookup: { - from: "taskNotifications", - localField: "tasks._id", - foreignField: "taskId", - as: "tasks.taskNotifications", + from: 'taskNotifications', + localField: 'tasks._id', + foreignField: 'taskId', + as: 'tasks.taskNotifications', }, }, { $group: { - _id: "$personId", - tasks: { $push: "$tasks" }, + _id: '$personId', + tasks: { $push: '$tasks' }, data: { - $first: "$$ROOT", + $first: '$$ROOT', }, }, }, { $addFields: { - "data.tasks": { + 'data.tasks': { $filter: { - input: "$tasks", - as: "task", - cond: { $ne: ["$$task", {}] }, + input: '$tasks', + as: 'task', + cond: { $ne: ['$$task', {}] }, }, }, }, }, { $replaceRoot: { - newRoot: "$data", + newRoot: '$data', }, }, ]); @@ -758,7 +747,7 @@ const taskHelper = function () { const getUserProfileFirstAndLastName = function (userId) { return userProfile.findById(userId).then((results) => { if (!results) { - return " "; + return ' '; } return `${results.firstName} ${results.lastName}`; }); diff --git a/src/helpers/timeEntryHelper.js b/src/helpers/timeEntryHelper.js index 7e14a94c8..7119db1a0 100644 --- a/src/helpers/timeEntryHelper.js +++ b/src/helpers/timeEntryHelper.js @@ -2,12 +2,12 @@ const moment = require('moment-timezone'); const timeEntry = require('../models/timeentry'); const timeEntryHelper = function () { - const getAllHoursLoggedForSpecifiedProject = function (projectId) { + const getAllHoursLoggedForSpecifiedTask = function (taskId) { const fromDate = moment('1900-01-01 00:00:00').format('YYYY-MM-DD'); const toDate = moment().tz('America/Los_Angeles').endOf('week').format('YYYY-MM-DD'); return timeEntry.find( { - projectId, + taskId, dateOfWork: { $gte: fromDate, $lte: toDate }, isTangible: true, }, @@ -20,7 +20,7 @@ const timeEntryHelper = function () { }; return { - getAllHoursLoggedForSpecifiedProject, + getAllHoursLoggedForSpecifiedTask, }; }; diff --git a/src/models/timeentry.js b/src/models/timeentry.js index 79504cc20..66d8d6575 100644 --- a/src/models/timeentry.js +++ b/src/models/timeentry.js @@ -7,9 +7,9 @@ const TimeEntry = new Schema({ entryType: { type: String, required: true, default: 'default' }, personId: { type: Schema.Types.ObjectId, ref: 'userProfile' }, projectId: { type: Schema.Types.ObjectId, ref: 'project' }, - wbsId: { type: Schema.Types.ObjectId, ref: 'project' }, - taskId: { type: Schema.Types.ObjectId, default: null, ref: 'wbs' }, - teamId: { type: Schema.Types.ObjectId, ref: 'task' }, + wbsId: { type: Schema.Types.ObjectId, default: null, ref: 'wbs' }, + taskId: { type: Schema.Types.ObjectId, default: null, ref: 'task' }, + teamId: { type: Schema.Types.ObjectId, ref: 'team' }, dateOfWork: { type: String, required: true }, totalSeconds: { type: Number }, notes: { type: String }, From 396aa6335d8e769217af5a5e2fcefd6afbd21dd2 Mon Sep 17 00:00:00 2001 From: wang9hu Date: Wed, 17 Jan 2024 22:45:27 -0800 Subject: [PATCH 36/36] add teamcode check in putUserProfile --- src/controllers/userProfileController.js | 30 ++++++++++++------------ 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/controllers/userProfileController.js b/src/controllers/userProfileController.js index c86d4c139..52da2c3c1 100644 --- a/src/controllers/userProfileController.js +++ b/src/controllers/userProfileController.js @@ -2,7 +2,7 @@ const moment = require('moment-timezone'); const mongoose = require('mongoose'); const bcrypt = require('bcryptjs'); -const fetch = require("node-fetch"); +const fetch = require('node-fetch'); const moment_ = require('moment'); const jwt = require('jsonwebtoken'); @@ -111,7 +111,6 @@ const userProfileController = function (UserProfile) { }; const postUserProfile = async function (req, res) { - if (!await hasPermission(req.body.requestor, 'postUserProfile')) { res.status(403).send('You are not authorized to create new users'); return; @@ -142,12 +141,12 @@ const userProfileController = function (UserProfile) { // In dev environment, if newly created user is Owner or Administrator, make fetch request to Beta login route with actualEmail and actual Password if (process.env.dbName === 'hgnData_dev') { if (req.body.role === 'Owner' || req.body.role === 'Administrator') { - const email = req.body.actualEmail - const password = req.body.actualPassword - const url = "https://hgn-rest-beta.azurewebsites.net/api/" + const email = req.body.actualEmail; + const password = req.body.actualPassword; + const url = 'https://hgn-rest-beta.azurewebsites.net/api/'; try { // Log in to Beta login route using provided credentials - const response = await fetch(url + 'login', { + const response = await fetch(`${url}login`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -276,14 +275,6 @@ const userProfileController = function (UserProfile) { return; } - const canEditTeamCode = req.body.requestor.role === 'Owner' - || req.body.requestor.role === 'Administrator' - || req.body.requestor.permissions?.frontPermissions.includes('editTeamCode'); - - if (!canEditTeamCode) { - res.status(403).send('You are not authorized to edit team code.'); - return; - } if (req.body.role === 'Owner' && !await hasPermission(req.body.requestor, 'addDeleteEditOwners')) { res.status(403).send('You are not authorized to update this user'); @@ -307,6 +298,15 @@ const userProfileController = function (UserProfile) { } } + const canEditTeamCode = req.body.requestor.role === 'Owner' + || req.body.requestor.role === 'Administrator' + || req.body.requestor.permissions?.frontPermissions.includes('editTeamCode'); + + if (!canEditTeamCode && record.teamCode !== req.body.teamCode) { + res.status(403).send('You are not authorized to edit team code.'); + return; + } + const originalinfringements = record.infringements ? record.infringements : []; @@ -890,7 +890,7 @@ const userProfileController = function (UserProfile) { const currentRefreshToken = jwt.sign(jwtPayload, JWT_SECRET); res.status(200).send({ refreshToken: currentRefreshToken }); }; - + return { postUserProfile, getUserProfiles,