From d02dc488a9b03be6a1467ff8fa4d77ae5b773eee Mon Sep 17 00:00:00 2001 From: Jerry Ren Date: Wed, 27 Sep 2023 16:32:17 -0400 Subject: [PATCH 1/9] 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 2/9] 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 3/9] 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 4/9] 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 5/9] 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 6/9] 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 35c3a61c0f82bb8403a8d705d561a5239173de98 Mon Sep 17 00:00:00 2001 From: abdelmounaim lallouache Date: Sat, 30 Dec 2023 19:44:01 -0600 Subject: [PATCH 7/9] 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 8/9] 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 22401e614bdd41c2debce5511f44b0b944d5f5f0 Mon Sep 17 00:00:00 2001 From: wang9hu Date: Thu, 11 Jan 2024 22:07:52 -0800 Subject: [PATCH 9/9] 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 },