From b1e760dca4e17bd983345044de0a8e2c4b7dc426 Mon Sep 17 00:00:00 2001 From: Samarth Bhadane Date: Fri, 29 Nov 2024 08:18:48 -0800 Subject: [PATCH 1/2] Sam Add Job CC Button --- .../jobNotificationListControllers.js | 118 ++++++++++++++++++ src/models/jobsNotificationList.js | 11 ++ src/routes/jobNotificationListRouter.js | 10 ++ src/startup/routes.js | 2 + 4 files changed, 141 insertions(+) create mode 100644 src/controllers/jobNotificationListControllers.js create mode 100644 src/models/jobsNotificationList.js create mode 100644 src/routes/jobNotificationListRouter.js diff --git a/src/controllers/jobNotificationListControllers.js b/src/controllers/jobNotificationListControllers.js new file mode 100644 index 000000000..610f85dcc --- /dev/null +++ b/src/controllers/jobNotificationListControllers.js @@ -0,0 +1,118 @@ +const mongoose = require('mongoose'); +const jobs = require('models/jobs'); +const JobsNotificationList = require('models/jobsNotificationList'); + +const { ObjectId } = mongoose.Types; + +const isOwner = async (req, res, next) => { + const { requestor } = req.body; // Correctly access the requestor + + try { + if (!requestor || requestor.role.toLowerCase() !== 'owner') { + return res.status(403).json({ error: 'Access denied' }); // Check ownership + } + next(); // Proceed if the user is an owner + } catch (err) { + res.status(500).json({ error: 'Internal server error' }); // Handle unexpected errors + } +}; + +const getJobWatchList = async (req, res) => { + const { jobId, category, title } = req.query; + + try { + const match = {}; + if (jobId) match._id = ObjectId(jobId); // Convert jobId to ObjectId + if (category) match.category = category; // Match by category + if (title) match.title = new RegExp(title, 'i'); // Search by job title (case-insensitive) + + const jobsWithCC = await jobs.aggregate([ + { $match: match }, // Apply filters + { + $lookup: { + from: 'jobsnotificationlists', // Match CC list collection + localField: '_id', + foreignField: 'jobId', + as: 'ccList', // Name for the joined data + }, + }, + { + $project: { + _id: 1, // Ensure the job's _id is included + title: 1, + category: 1, + datePosted: 1, + 'ccList._id': 1, // Include the _id of each CC entry + 'ccList.email': 1, // Only include emails from the CC list + }, + }, + { $sort: { datePosted: -1 } }, // Sort by most recent jobs + ]); + + res.status(200).json(jobsWithCC); + } catch (err) { + console.error(err); // Log error for debugging + res.status(500).json({ error: 'Internal server error' }); + } + }; + +const addEmailToCCList = async (req, res) => { + const { email, jobId, category } = req.body; + + if (!email || (!jobId && !category)) { + return res.status(400).json({ error: 'Email, Job ID, and Category are required' }); + } + + try { + if (jobId) { + const job = await jobs.findById(jobId); + if (!job) { + return res.status(404).json({ error: 'Job not found' }); + } + } + + if (category) { + const categoryExists = await jobs.exists({ category }); + if (!categoryExists) { + return res.status(404).json({ error: 'Category not found' }); + } + } + + const ccEntry = new JobsNotificationList({ + email, + jobId: jobId || null, + category: category || null, + }); + + await ccEntry.save(); + + res.status(201).json({ message: 'Email added to CC list successfully' }); + } catch (err) { + if (err.code === 11000) { + return res.status(409).json({ error: 'Email already exists in the CC list' }); + } + res.status(500).json({ error: 'Internal server error' }); + } +}; + +const removeEmailFromCCList = async (req, res) => { + const { id } = req.params; // ID of the CC entry + + try { + const result = await JobsNotificationList.findByIdAndDelete(id); + if (!result) { + return res.status(404).json({ error: 'CC entry not found' }); + } + res.status(200).json({ message: 'CC entry removed successfully' }); + } catch (err) { + res.status(500).json({ error: 'Internal server error' }); + } + }; + + +module.exports = { + isOwner, + getJobWatchList, + addEmailToCCList, + removeEmailFromCCList +}; diff --git a/src/models/jobsNotificationList.js b/src/models/jobsNotificationList.js new file mode 100644 index 000000000..e45226954 --- /dev/null +++ b/src/models/jobsNotificationList.js @@ -0,0 +1,11 @@ +const mongoose = require('mongoose'); + +const jobsNotificationListSchema = new mongoose.Schema({ + email: { type: String, required: true }, + jobId: { type: mongoose.Schema.Types.ObjectId, ref: 'Job', default: null }, + category: { type: String, default: null }, +}); + +jobsNotificationListSchema.index({ email: 1, jobId: 1, category: 1 }, { unique: true }); + +module.exports = mongoose.model('JobsNotificationList', jobsNotificationListSchema); diff --git a/src/routes/jobNotificationListRouter.js b/src/routes/jobNotificationListRouter.js new file mode 100644 index 000000000..8102253b7 --- /dev/null +++ b/src/routes/jobNotificationListRouter.js @@ -0,0 +1,10 @@ +const express = require('express'); +const jobNotificationListControllers = require('../controllers/jobNotificationListControllers'); + +const router = express.Router(); + +router.get('/', jobNotificationListControllers.isOwner, jobNotificationListControllers.getJobWatchList); +router.post('/', jobNotificationListControllers.isOwner, jobNotificationListControllers.addEmailToCCList); +router.delete('/:id', jobNotificationListControllers.isOwner, jobNotificationListControllers.removeEmailFromCCList); + +module.exports = router; \ No newline at end of file diff --git a/src/startup/routes.js b/src/startup/routes.js index b307ac4f4..00067269a 100644 --- a/src/startup/routes.js +++ b/src/startup/routes.js @@ -78,6 +78,7 @@ const permissionChangeLogRouter = require('../routes/permissionChangeLogsRouter' permissionChangeLog, ); const isEmailExistsRouter = require('../routes/isEmailExistsRouter')(); +const jobNotificationListRouter = require('../routes/jobNotificationListRouter'); const taskEditSuggestion = require('../models/taskEditSuggestion'); const taskEditSuggestionRouter = require('../routes/taskEditSuggestionRouter')(taskEditSuggestion); @@ -163,6 +164,7 @@ module.exports = function (app) { app.use('/api', followUpRouter); app.use('/api', blueSquareEmailAssignmentRouter); app.use('/api/jobs', jobsRouter) + app.use('/api/job-notification-list/', jobNotificationListRouter) // bm dashboard app.use('/api/bm', bmLoginRouter); app.use('/api/bm', bmMaterialsRouter); From dee0a4cf3a4a652797c72f560eed49664a2f40a4 Mon Sep 17 00:00:00 2001 From: Samarth Bhadane Date: Thu, 5 Dec 2024 02:05:52 -0800 Subject: [PATCH 2/2] CC Page --- .../jobNotificationListControllers.js | 69 +++++++++++++++---- src/routes/jobNotificationListRouter.js | 3 +- 2 files changed, 57 insertions(+), 15 deletions(-) diff --git a/src/controllers/jobNotificationListControllers.js b/src/controllers/jobNotificationListControllers.js index 610f85dcc..f87e7b10e 100644 --- a/src/controllers/jobNotificationListControllers.js +++ b/src/controllers/jobNotificationListControllers.js @@ -48,7 +48,7 @@ const getJobWatchList = async (req, res) => { }, { $sort: { datePosted: -1 } }, // Sort by most recent jobs ]); - + res.status(200).json(jobsWithCC); } catch (err) { console.error(err); // Log error for debugging @@ -56,32 +56,26 @@ const getJobWatchList = async (req, res) => { } }; -const addEmailToCCList = async (req, res) => { - const { email, jobId, category } = req.body; +const addCCByJob = async (req, res) => { + const { email, jobId } = req.body; - if (!email || (!jobId && !category)) { + if (!email || !jobId) { return res.status(400).json({ error: 'Email, Job ID, and Category are required' }); } try { + let job; if (jobId) { - const job = await jobs.findById(jobId); + job = await jobs.findById(jobId); if (!job) { return res.status(404).json({ error: 'Job not found' }); } } - if (category) { - const categoryExists = await jobs.exists({ category }); - if (!categoryExists) { - return res.status(404).json({ error: 'Category not found' }); - } - } - const ccEntry = new JobsNotificationList({ email, jobId: jobId || null, - category: category || null, + category: job.category || null, }); await ccEntry.save(); @@ -95,6 +89,52 @@ const addEmailToCCList = async (req, res) => { } }; +const addCCByCategory = async (req, res) => { + const { email, category } = req.body; + + // Validate input + if (!email || !category) { + return res.status(400).json({ error: 'Email and Category are required' }); + } + + try { + // Check if the category exists + const categoryExists = await jobs.exists({ category }); + if (!categoryExists) { + return res.status(404).json({ error: 'Category not found' }); + } + + // Find all jobs in the specified category + const jobArr = await jobs.find({ category }); + + if (jobArr.length === 0) { + return res.status(404).json({ error: 'No jobs found in this category' }); + } + + // Create bulk operations for all jobs in the category + const bulkOps = jobArr.map((job) => ({ + updateOne: { + filter: { email, jobId: job._id, category: job.category }, + update: { $setOnInsert: { email, jobId: job._id, category: job.category } }, + upsert: true, // Perform upsert to avoid duplicates + }, + })); + + // Execute bulkWrite operation + const result = await JobsNotificationList.bulkWrite(bulkOps); + + res.status(201).json({ + message: 'Email added to CC list successfully', + matchedCount: result.matchedCount, + modifiedCount: result.modifiedCount, + upsertedCount: result.upsertedCount, + }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Internal server error' }); + } + }; + const removeEmailFromCCList = async (req, res) => { const { id } = req.params; // ID of the CC entry @@ -113,6 +153,7 @@ const removeEmailFromCCList = async (req, res) => { module.exports = { isOwner, getJobWatchList, - addEmailToCCList, + addCCByJob, + addCCByCategory, removeEmailFromCCList }; diff --git a/src/routes/jobNotificationListRouter.js b/src/routes/jobNotificationListRouter.js index 8102253b7..06cfad50c 100644 --- a/src/routes/jobNotificationListRouter.js +++ b/src/routes/jobNotificationListRouter.js @@ -4,7 +4,8 @@ const jobNotificationListControllers = require('../controllers/jobNotificationLi const router = express.Router(); router.get('/', jobNotificationListControllers.isOwner, jobNotificationListControllers.getJobWatchList); -router.post('/', jobNotificationListControllers.isOwner, jobNotificationListControllers.addEmailToCCList); +router.post('/job', jobNotificationListControllers.isOwner, jobNotificationListControllers.addCCByJob); +router.post('/category', jobNotificationListControllers.isOwner, jobNotificationListControllers.addCCByCategory); router.delete('/:id', jobNotificationListControllers.isOwner, jobNotificationListControllers.removeEmailFromCCList); module.exports = router; \ No newline at end of file