From 28ad93b98d5ea6608ef3e7633992bfa6b28f2180 Mon Sep 17 00:00:00 2001 From: Anoushka Gupta Date: Thu, 28 Nov 2024 20:21:33 -0800 Subject: [PATCH 1/4] add /summaries API --- src/controllers/jobsController.js | 51 +++++++++++++++++++++++++++++++ src/routes/jobsRouter.js | 2 +- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/controllers/jobsController.js b/src/controllers/jobsController.js index f782e0eeb..4d6d993b0 100644 --- a/src/controllers/jobsController.js +++ b/src/controllers/jobsController.js @@ -39,6 +39,55 @@ const getJobs = async (req, res) => { } }; +// Controller to fetch job summaries with pagination, search, filtering, and sorting +const getJobSummaries = async (req, res) => { + const { search = '', page = 1, limit = 18, category = '' } = req.query; + + try { + // Validate pagination parameters + const pageNumber = Math.max(1, parseInt(page, 10)); // Page number should be at least 1 + const limitNumber = Math.max(1, parseInt(limit, 10)); // Limit number of results per page + + // Construct the query object + const query = {}; + if (search) query.title = { $regex: search, $options: 'i' }; // Search based on title (case-insensitive) + if (category) query.category = category; // Filter by category if provided + + // Sorting logic based on multiple criteria + const sortCriteria = { + title: 1, // Sort by title alphabetically + datePosted: -1, // If titles are the same, sort by datePosted (newest first) + featured: -1 // If title and datePosted are the same, prioritize featured jobs + }; + + // Fetch the total number of jobs matching the query for pagination + const totalJobs = await Job.countDocuments(query); + + // Fetch job summaries (only the essential fields) for the current page + const jobs = await Job.find(query) + .select('title category location description datePosted featured') // Include fields as needed + .sort(sortCriteria) // Apply sorting logic + .skip((pageNumber - 1) * limitNumber) // Skip jobs based on the page number + .limit(limitNumber); // Limit the results per page + + // Return the results along with pagination metadata + res.json({ + jobs, + pagination: { + totalJobs, + totalPages: Math.ceil(totalJobs / limitNumber), // Calculate total number of pages + currentPage: pageNumber, + limit: limitNumber, + hasNextPage: pageNumber < Math.ceil(totalJobs / limitNumber), + hasPreviousPage: pageNumber > 1, + }, + }); + } catch (error) { + res.status(500).json({ error: 'Failed to fetch job summaries', details: error.message }); + } +}; + + // Controller to fetch job details by ID const getJobById = async (req, res) => { const { id } = req.params; @@ -107,6 +156,7 @@ const deleteJob = async (req, res) => { } }; + // Export controllers as a plain object module.exports = { getJobs, @@ -114,4 +164,5 @@ module.exports = { createJob, updateJob, deleteJob, + getJobSummaries }; diff --git a/src/routes/jobsRouter.js b/src/routes/jobsRouter.js index 6dfa69a85..d8b215a76 100644 --- a/src/routes/jobsRouter.js +++ b/src/routes/jobsRouter.js @@ -5,9 +5,9 @@ const router = express.Router(); // Define routes router.get('/', jobsController.getJobs); +router.get('/summaries', jobsController.getJobSummaries); // GET request to fetch job summaries router.get('/:id', jobsController.getJobById); router.post('/', jobsController.createJob); router.put('/:id', jobsController.updateJob); router.delete('/:id', jobsController.deleteJob); - module.exports = router; From e266894cb2d7c4c3183b8ff0bbfbf5b634dc6f9a Mon Sep 17 00:00:00 2001 From: Anoushka Gupta Date: Fri, 20 Dec 2024 20:59:21 -0800 Subject: [PATCH 2/4] add /suggestions and /resetfilter API --- src/controllers/jobsController.js | 83 ++++++++++++++++++++++++------- src/routes/jobsRouter.js | 4 +- 2 files changed, 69 insertions(+), 18 deletions(-) diff --git a/src/controllers/jobsController.js b/src/controllers/jobsController.js index 4d6d993b0..1918f2a9d 100644 --- a/src/controllers/jobsController.js +++ b/src/controllers/jobsController.js @@ -44,33 +44,29 @@ const getJobSummaries = async (req, res) => { const { search = '', page = 1, limit = 18, category = '' } = req.query; try { - // Validate pagination parameters - const pageNumber = Math.max(1, parseInt(page, 10)); // Page number should be at least 1 - const limitNumber = Math.max(1, parseInt(limit, 10)); // Limit number of results per page + const pageNumber = Math.max(1, parseInt(page, 10)); + const limitNumber = Math.max(1, parseInt(limit, 10)); // Construct the query object const query = {}; - if (search) query.title = { $regex: search, $options: 'i' }; // Search based on title (case-insensitive) - if (category) query.category = category; // Filter by category if provided + if (search) query.title = { $regex: search, $options: 'i' }; + if (category) query.category = category; - // Sorting logic based on multiple criteria + // Sorting logic const sortCriteria = { - title: 1, // Sort by title alphabetically - datePosted: -1, // If titles are the same, sort by datePosted (newest first) - featured: -1 // If title and datePosted are the same, prioritize featured jobs + title: 1, + datePosted: -1, + featured: -1 }; // Fetch the total number of jobs matching the query for pagination const totalJobs = await Job.countDocuments(query); - - // Fetch job summaries (only the essential fields) for the current page const jobs = await Job.find(query) - .select('title category location description datePosted featured') // Include fields as needed - .sort(sortCriteria) // Apply sorting logic - .skip((pageNumber - 1) * limitNumber) // Skip jobs based on the page number - .limit(limitNumber); // Limit the results per page + .select('title category location description datePosted featured') + .sort(sortCriteria) + .skip((pageNumber - 1) * limitNumber) + .limit(limitNumber); - // Return the results along with pagination metadata res.json({ jobs, pagination: { @@ -87,6 +83,57 @@ const getJobSummaries = async (req, res) => { } }; +// Controller to fetch job title suggestions for a dropdown +const getJobTitleSuggestions = async (req, res) => { + const { query = '' } = req.query; + + try { + const suggestions = await Job.find({ title: { $regex: query, $options: 'i' } }) + .distinct('title'); + + res.json({ suggestions }); + } catch (error) { + res.status(500).json({ error: 'Failed to fetch job title suggestions', details: error.message }); + } +}; + +const resetJobsFilters = async (req, res) => { + const { page = 1, limit = 18 } = req.query; + + try { + // Validate pagination parameters + const pageNumber = Math.max(1, parseInt(page, 10)); + const limitNumber = Math.max(1, parseInt(limit, 10)); + + // Sorting logic + const sortCriteria = { + title: 1, + datePosted: -1, + featured: -1 + }; + // Fetch all jobs without filtering + const totalJobs = await Job.countDocuments({}); + const jobs = await Job.find({}) + .sort(sortCriteria) + .skip((pageNumber - 1) * limitNumber) + .limit(limitNumber); + + // Respond with all jobs and pagination metadata + res.json({ + jobs, + pagination: { + totalJobs, + totalPages: Math.ceil(totalJobs / limitNumber), + currentPage: pageNumber, + limit: limitNumber, + hasNextPage: pageNumber < Math.ceil(totalJobs / limitNumber), + hasPreviousPage: pageNumber > 1, + }, + }); + } catch (error) { + res.status(500).json({ error: 'Failed to reset filters or reload jobs', details: error.message }); + } +}; // Controller to fetch job details by ID const getJobById = async (req, res) => { @@ -160,9 +207,11 @@ const deleteJob = async (req, res) => { // Export controllers as a plain object module.exports = { getJobs, + getJobTitleSuggestions, getJobById, createJob, updateJob, deleteJob, - getJobSummaries + getJobSummaries, + resetJobsFilters }; diff --git a/src/routes/jobsRouter.js b/src/routes/jobsRouter.js index d8b215a76..7261e69c6 100644 --- a/src/routes/jobsRouter.js +++ b/src/routes/jobsRouter.js @@ -4,8 +4,10 @@ const jobsController = require('../controllers/jobsController'); // Adjust the p const router = express.Router(); // Define routes +router.get('/suggestions', jobsController.getJobTitleSuggestions); +router.get('/reset-filters', jobsController.resetJobsFilters); +router.get('/summaries', jobsController.getJobSummaries); router.get('/', jobsController.getJobs); -router.get('/summaries', jobsController.getJobSummaries); // GET request to fetch job summaries router.get('/:id', jobsController.getJobById); router.post('/', jobsController.createJob); router.put('/:id', jobsController.updateJob); From 67973a16d8d6c6b6402e2899d6b1e3b315a8ed5a Mon Sep 17 00:00:00 2001 From: Anoushka Gupta Date: Sat, 21 Dec 2024 00:47:16 -0800 Subject: [PATCH 3/4] fix merge conflict --- src/controllers/jobsController.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/jobsController.js b/src/controllers/jobsController.js index dc2b331b7..0fe1583e7 100644 --- a/src/controllers/jobsController.js +++ b/src/controllers/jobsController.js @@ -225,6 +225,6 @@ module.exports = { updateJob, deleteJob, getJobSummaries, - resetJobsFilters + resetJobsFilters, getCategories }; From b0641f56dcf48fcdc1744477efeab57d9348a055 Mon Sep 17 00:00:00 2001 From: Anoushka Gupta Date: Wed, 25 Dec 2024 02:20:29 -0800 Subject: [PATCH 4/4] fix order of job controllers --- src/controllers/jobsController.js | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/controllers/jobsController.js b/src/controllers/jobsController.js index 0fe1583e7..81e7c3cb3 100644 --- a/src/controllers/jobsController.js +++ b/src/controllers/jobsController.js @@ -135,6 +135,19 @@ const resetJobsFilters = async (req, res) => { } }; +const getCategories = async (req, res) => { + try { + const categories = await Job.distinct('category', {}); + + // Sort categories alphabetically + categories.sort((a, b) => a.localeCompare(b)); + + res.status(200).json({ categories }); + } catch (error) { + console.error('Error fetching categories:', error); + res.status(500).json({ message: 'Failed to fetch categories' }); + } +}; // Controller to fetch job details by ID const getJobById = async (req, res) => { const { id } = req.params; @@ -202,19 +215,7 @@ const deleteJob = async (req, res) => { } }; -const getCategories = async (req, res) => { - try { - const categories = await Job.distinct('category', {}); - - // Sort categories alphabetically - categories.sort((a, b) => a.localeCompare(b)); - res.status(200).json({ categories }); - } catch (error) { - console.error('Error fetching categories:', error); - res.status(500).json({ message: 'Failed to fetch categories' }); - } -}; // Export controllers as a plain object module.exports = {