diff --git a/src/controllers/userProfileController.js b/src/controllers/userProfileController.js index 8226aee65..f8b35ed02 100644 --- a/src/controllers/userProfileController.js +++ b/src/controllers/userProfileController.js @@ -196,6 +196,38 @@ const userProfileController = function (UserProfile, Project) { .catch((error) => res.status(404).send(error)); }; + /** + * Controller function to retrieve basic user profile information. + * This endpoint checks if the user has the necessary permissions to access user profiles. + * If authorized, it queries the database to fetch only the required fields: + * _id, firstName, lastName, isActive, startDate, and endDate, sorted by last name. + */ + const getUserProfileBasicInfo = async function (req, res) { + if (!(await checkPermission(req, 'getUserProfiles'))) { + forbidden(res, 'You are not authorized to view all users'); + return; + } + + await UserProfile.find({}, '_id firstName lastName isActive startDate createdDate endDate') + .sort({ + lastName: 1, + }) + .then((results) => { + if (!results) { + if (cache.getCache('allusers')) { + const getData = JSON.parse(cache.getCache('allusers')); + res.status(200).send(getData); + return; + } + res.status(500).send({ error: 'User result was invalid' }); + return; + } + cache.setCache('allusers', JSON.stringify(results)); + res.status(200).send(results); + }) + .catch((error) => res.status(404).send(error)); + }; + const getProjectMembers = async function (req, res) { if (!(await hasPermission(req.body.requestor, 'getProjectMembers'))) { res.status(403).send('You are not authorized to view all users'); @@ -326,6 +358,7 @@ const userProfileController = function (UserProfile, Project) { up.adminLinks = req.body.adminLinks; up.teams = Array.from(new Set(req.body.teams)); up.projects = Array.from(new Set(req.body.projects)); + up.teamCode = req.body.teamCode; up.createdDate = req.body.createdDate; up.startDate = req.body.startDate ? req.body.startDate : req.body.createdDate; up.email = req.body.email; @@ -505,7 +538,7 @@ const userProfileController = function (UserProfile, Project) { } }); - // Since we leverage cache for all team code retrival (refer func getAllTeamCode()), + // Since we leverage cache for all team code retrival (refer func getAllTeamCode()), // we need to remove the cache when team code is updated in case of new team code generation if (req.body.teamCode) { // remove teamCode cache when new team assigned @@ -644,7 +677,7 @@ const userProfileController = function (UserProfile, Project) { } if (req.body.startDate !== undefined && record.startDate !== req.body.startDate) { - record.startDate = moment(req.body.startDate).toDate(); + record.startDate = moment.tz(req.body.startDate, 'America/Los_Angeles').toDate(); // Make sure weeklycommittedHoursHistory isn't empty if (record.weeklycommittedHoursHistory.length === 0) { const newEntry = { @@ -667,7 +700,7 @@ const userProfileController = function (UserProfile, Project) { if (req.body.endDate !== undefined) { if (yearMonthDayDateValidator(req.body.endDate)) { - record.endDate = moment(req.body.endDate).toDate(); + record.endDate = moment.tz(req.body.endDate, 'America/Los_Angeles').toDate(); if (isUserInCache) { userData.endDate = record.endDate.toISOString(); } @@ -684,12 +717,7 @@ const userProfileController = function (UserProfile, Project) { userData.startDate = record.startDate.toISOString(); } } - if ( - req.body.infringements !== undefined && - (await hasPermission(req.body.requestor, 'infringementAuthorizer')) - ) { - record.infringements = req.body.infringements; - } + let updatedDiff = null; if (PROTECTED_EMAIL_ACCOUNT.includes(record.email)) { updatedDiff = record.modifiedPaths(); @@ -727,7 +755,17 @@ const userProfileController = function (UserProfile, Project) { 'update', ); }) - .catch((error) => res.status(400).send(error)); + .catch((error) => { + if (error.name === 'ValidationError' && error.errors.lastName) { + const errors = Object.values(error.errors).map((er) => er.message); + return res.status(400).json({ + message: 'Validation Error', + error: errors, + }); + } + console.error('Failed to save record:', error); + return res.status(400).json({ error: 'Failed to save record.' }); + }); }); }; @@ -843,11 +881,11 @@ const userProfileController = function (UserProfile, Project) { const getUserById = function (req, res) { const userid = req.params.userId; - if (cache.getCache(`user-${userid}`)) { - const getData = JSON.parse(cache.getCache(`user-${userid}`)); - res.status(200).send(getData); - return; - } + // if (cache.getCache(`user-${userid}`)) { + // const getData = JSON.parse(cache.getCache(`user-${userid}`)); + // res.status(200).send(getData); + // return; + // } UserProfile.findById(userid, '-password -refreshTokens -lastModifiedDate -__v') .populate([ @@ -877,6 +915,15 @@ const userProfileController = function (UserProfile, Project) { select: '_id badgeName type imageUrl description ranking showReport', }, }, + { + path: 'infringements', // Populate infringements field + select: 'date description', + options: { + sort: { + date: -1, // Sort by date descending if needed + }, + }, + }, ]) .exec() .then((results) => { @@ -1021,7 +1068,7 @@ const userProfileController = function (UserProfile, Project) { const hasUpdatePasswordPermission = await hasPermission(requestor, 'updatePassword'); // if they're updating someone else's password, they need the 'updatePassword' permission. - if (!hasUpdatePasswordPermission) { + if (userId !== requestor.requestorId && !hasUpdatePasswordPermission) { return res.status(403).send({ error: "You are unauthorized to update this user's password", }); @@ -1156,7 +1203,18 @@ const userProfileController = function (UserProfile, Project) { const activationDate = req.body.reactivationDate; const { endDate } = req.body; const isSet = req.body.isSet === 'FinalDay'; - + let activeStatus = status; + let emailThreeWeeksSent = false; + if (endDate && status) { + const dateObject = new Date(endDate); + dateObject.setHours(dateObject.getHours() + 7); + const setEndDate = dateObject; + if (moment().isAfter(moment(setEndDate).add(1, 'days'))) { + activeStatus = false; + } else if (moment().isBefore(moment(endDate).subtract(3, 'weeks'))) { + emailThreeWeeksSent = true; + } + } if (!mongoose.Types.ObjectId.isValid(userId)) { res.status(400).send({ error: 'Bad Request', @@ -1202,13 +1260,14 @@ const userProfileController = function (UserProfile, Project) { logger.logException(err, 'Unexpected error in finding menagement team'); } - UserProfile.findById(userId, 'isActive email firstName lastName') + UserProfile.findById(userId, 'isActive email firstName lastName finalEmailThreeWeeksSent') .then((user) => { user.set({ - isActive: status, + isActive: activeStatus, reactivationDate: activationDate, endDate, isSet, + finalEmailThreeWeeksSent: emailThreeWeeksSent, }); user .save() @@ -1232,6 +1291,8 @@ const userProfileController = function (UserProfile, Project) { user.email, recipients, isSet, + activationDate, + emailThreeWeeksSent, ); auditIfProtectedAccountUpdated( req.body.requestor.requestorId, @@ -1539,6 +1600,155 @@ const userProfileController = function (UserProfile, Project) { } }; + const addInfringements = async function (req, res) { + if (!(await hasPermission(req.body.requestor, 'addInfringements'))) { + res.status(403).send('You are not authorized to add blue square'); + return; + } + const userid = req.params.userId; + + cache.removeCache(`user-${userid}`); + + if (req.body.blueSquare === undefined) { + res.status(400).send('Invalid Data'); + return; + } + + UserProfile.findById(userid, async (err, record) => { + if (err || !record) { + res.status(404).send('No valid records found'); + return; + } + // find userData in cache + const isUserInCache = cache.hasCache('allusers'); + let allUserData; + let userData; + let userIdx; + if (isUserInCache) { + allUserData = JSON.parse(cache.getCache('allusers')); + userIdx = allUserData.findIndex((users) => users._id === userid); + userData = allUserData[userIdx]; + } + + const originalinfringements = record?.infringements ?? []; + record.infringements = originalinfringements.concat(req.body.blueSquare); + + record + .save() + .then((results) => { + userHelper.notifyInfringements( + originalinfringements, + results.infringements, + results.firstName, + results.lastName, + results.email, + results.role, + results.startDate, + results.jobTitle[0], + results.weeklycommittedHours, + ); + res.status(200).json({ + _id: record._id, + }); + + // update alluser cache if we have cache + if (isUserInCache) { + allUserData.splice(userIdx, 1, userData); + cache.setCache('allusers', JSON.stringify(allUserData)); + } + }) + .catch((error) => res.status(400).send(error)); + }); + }; + + const editInfringements = async function (req, res) { + if (!(await hasPermission(req.body.requestor, 'editInfringements'))) { + res.status(403).send('You are not authorized to edit blue square'); + return; + } + const { userId, blueSquareId } = req.params; + const { dateStamp, summary } = req.body; + + UserProfile.findById(userId, async (err, record) => { + if (err || !record) { + res.status(404).send('No valid records found'); + return; + } + + const originalinfringements = record?.infringements ?? []; + + record.infringements = originalinfringements.map((blueSquare) => { + if (blueSquare._id.equals(blueSquareId)) { + blueSquare.date = dateStamp ?? blueSquare.date; + blueSquare.description = summary ?? blueSquare.description; + } + return blueSquare; + }); + + record + .save() + .then((results) => { + userHelper.notifyInfringements( + originalinfringements, + results.infringements, + results.firstName, + results.lastName, + results.email, + results.role, + results.startDate, + results.jobTitle[0], + results.weeklycommittedHours, + ); + res.status(200).json({ + _id: record._id, + }); + }) + .catch((error) => res.status(400).send(error)); + }); + }; + + const deleteInfringements = async function (req, res) { + if (!(await hasPermission(req.body.requestor, 'deleteInfringements'))) { + res.status(403).send('You are not authorized to delete blue square'); + return; + } + const { userId, blueSquareId } = req.params; + // console.log(userId, blueSquareId); + + UserProfile.findById(userId, async (err, record) => { + if (err || !record) { + res.status(404).send('No valid records found'); + return; + } + + const originalinfringements = record?.infringements ?? []; + + record.infringements = originalinfringements.filter( + (infringement) => !infringement._id.equals(blueSquareId), + ); + + record + .save() + .then((results) => { + userHelper.notifyInfringements( + originalinfringements, + results.infringements, + results.firstName, + results.lastName, + results.email, + results.role, + results.startDate, + results.jobTitle[0], + results.weeklycommittedHours, + ); + res.status(200).json({ + _id: record._id, + }); + }) + .catch((error) => res.status(400).send(error)); + }); + }; + const getProjectsByPerson = async function (req, res) { try { const { name } = req.params; @@ -1597,21 +1807,74 @@ const userProfileController = function (UserProfile, Project) { return teamCodes; } const distinctTeamCodes = await UserProfile.distinct('teamCode', { - teamCode: { $ne: null } + teamCode: { $ne: null }, }); cache.setCache('teamCodes', JSON.stringify(distinctTeamCodes)); return distinctTeamCodes; } catch (error) { throw new Error('Encountered an error to get all team codes, please try again!'); } - } + }; const getAllTeamCode = async function (req, res) { try { const distinctTeamCodes = await getAllTeamCodeHelper(); return res.status(200).send({ message: 'Found', distinctTeamCodes }); } catch (error) { - return res.status(500).send({ message: 'Encountered an error to get all team codes, please try again!' }); + return res + .status(500) + .send({ message: 'Encountered an error to get all team codes, please try again!' }); + } + }; + + const getUserByAutocomplete = (req, res) => { + const { searchText } = req.params; + + if (!searchText) { + return res.status(400).send({ message: 'Search text is required' }); + } + + const regex = new RegExp(searchText, 'i'); // Case-insensitive regex for partial matching + + UserProfile.find( + { + $or: [ + { firstName: { $regex: regex } }, + { lastName: { $regex: regex } }, + { + $expr: { + $regexMatch: { + input: { $concat: ['$firstName', ' ', '$lastName'] }, + regex: searchText, + options: 'i', + }, + }, + }, + ], + }, + '_id firstName lastName', // Projection to limit fields returned + ) + .limit(10) // Limit results for performance + .then((results) => { + res.status(200).send(results); + }) + .catch(() => { + res.status(500).send({ error: 'Internal Server Error' }); + }); + }; + + const updateUserInformation = async function (req,res){ + try { + const data=req.body; + data.map(async (e)=> { + const result = await UserProfile.findById(e.user_id); + result[e.item]=e.value + await result.save(); + }) + res.status(200).send({ message: 'Update successful'}); + } catch (error) { + console.log(error) + return res.status(500) } } @@ -1637,9 +1900,15 @@ const userProfileController = function (UserProfile, Project) { getUserByFullName, changeUserRehireableStatus, authorizeUser, + addInfringements, + editInfringements, + deleteInfringements, getProjectsByPerson, getAllTeamCode, getAllTeamCodeHelper, + getUserByAutocomplete, + getUserProfileBasicInfo, + updateUserInformation, }; }; diff --git a/src/routes/userProfileRouter.js b/src/routes/userProfileRouter.js index bf6f79237..2d68d2da1 100644 --- a/src/routes/userProfileRouter.js +++ b/src/routes/userProfileRouter.js @@ -23,6 +23,9 @@ const routes = function (userProfile, project) { controller.postUserProfile, ); + userProfileRouter.route('/userProfile/update').patch(controller.updateUserInformation); + // Endpoint to retrieve basic user profile information + userProfileRouter.route('/userProfile/basicInfo').get(controller.getUserProfileBasicInfo); userProfileRouter .route('/userProfile/:userId') .get(controller.getUserById) @@ -102,10 +105,21 @@ const routes = function (userProfile, project) { .route('/userProfile/authorizeUser/weeeklySummaries') .post(controller.authorizeUser); + userProfileRouter.route('/userProfile/:userId/addInfringement').post(controller.addInfringements); + + userProfileRouter + .route('/userProfile/:userId/infringements/:blueSquareId') + .put(controller.editInfringements) + .delete(controller.deleteInfringements); + userProfileRouter.route('/userProfile/projects/:name').get(controller.getProjectsByPerson); userProfileRouter.route('/userProfile/teamCode/list').get(controller.getAllTeamCode); + userProfileRouter + .route('/userProfile/autocomplete/:searchText') + .get(controller.getUserByAutocomplete); + return userProfileRouter; };