Skip to content

Commit

Permalink
feat(admin): add backend search for tables
Browse files Browse the repository at this point in the history
  • Loading branch information
iam-abin committed Oct 31, 2024
1 parent a84bbfa commit 4e0c241
Show file tree
Hide file tree
Showing 39 changed files with 642 additions and 377 deletions.
2 changes: 0 additions & 2 deletions admin/src/controllers/candidate/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import getAllCandidatesController from './getCandidates.controller';
import getCandidateByIdController from './viewProfile.controller';
import searchCandidatesController from './search.controller';
import candidateBlockUnblockController from './blockUnblock.controller';

import { IDependency } from '../../frameworks/types/dependency';
Expand All @@ -9,7 +8,6 @@ export = (dependencies: IDependency) => {
return {
getAllCandidatesController: getAllCandidatesController(dependencies),
getCandidateByIdController: getCandidateByIdController(dependencies),
searchCandidatesController: searchCandidatesController(dependencies),
candidateBlockUnblockController: candidateBlockUnblockController(dependencies),
};
};
2 changes: 2 additions & 0 deletions admin/src/controllers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import recruiterControllers from './recruiter';
import dashboardControllers from './admin';
import membershipControllers from './membership';
import paymentControllers from './payment';
import searchControllers from './search';

export {
candidateControllers,
Expand All @@ -12,4 +13,5 @@ export {
dashboardControllers,
membershipControllers,
paymentControllers,
searchControllers
};
8 changes: 8 additions & 0 deletions admin/src/controllers/search/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import searchController from "./search.controller"
import { IDependency } from '../../frameworks/types/dependency';

export = (dependencies: IDependency) => {
return {
searchController: searchController(dependencies),
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,22 @@ import { IDependency } from '../../frameworks/types/dependency';

export = (dependencies: IDependency) => {
const {
useCases: { searchCandidatesUseCase },
useCases: { searchUseCase },
} = dependencies;

return async (req: Request, res: Response) => {
const { type: resourceType } = req.params;
const { searchKey } = req.query;

const { candidates, numberOfPages } = await searchCandidatesUseCase(dependencies).execute(
searchKey,
const { result, numberOfPages } = await searchUseCase(dependencies).execute(
resourceType,
searchKey as string,
Number(req.params.page) || 1,
Number(req.params.limit) || 4,
);

res.status(200).json({
message: 'candidates fetched successfully',
data: { candidates, numberOfPages },
message: 'search results fetched successfully',
data: { result, numberOfPages },
});
};
};
8 changes: 4 additions & 4 deletions admin/src/frameworks/database/models/job.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import mongoose from 'mongoose';
import { IJob } from '../../types/job';

export type JobDocument = mongoose.Document & Omit<IJob, 'jobId'>;
export type IJobDocument = mongoose.Document & Omit<IJob, 'jobId'>;

const jobSchema = new mongoose.Schema(
{
Expand Down Expand Up @@ -39,13 +39,13 @@ const jobSchema = new mongoose.Schema(
},
);

interface JobModel extends mongoose.Model<JobDocument> {
buildJob(attributes: IJob): JobDocument;
interface JobModel extends mongoose.Model<IJobDocument> {
buildJob(attributes: IJob): IJobDocument;
}

jobSchema.statics.buildJob = (attributes: IJob) => {
const { jobId, ...rest } = attributes;
return new JobModel({ ...rest, _id: jobId });
};

export const JobModel = mongoose.model<JobDocument, JobModel>('Job', jobSchema);
export const JobModel = mongoose.model<IJobDocument, JobModel>('Job', jobSchema);
1 change: 0 additions & 1 deletion admin/src/frameworks/express/routes/candidate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export const candidateRouter = (dependencies: IDependency) => {
const candidateController = candidateControllers(dependencies);

// candidate
router.get('/candidates/search/:page/:limit', candidateController.searchCandidatesController);
router.get('/candidates/:page/:limit', candidateController.getAllCandidatesController);
router.get('/view-profile/:userId', candidateController.getCandidateByIdController);
router.put('/block-unblock/:userId', candidateController.candidateBlockUnblockController);
Expand Down
3 changes: 3 additions & 0 deletions admin/src/frameworks/express/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { membershipPlanRouter } from './membershipplan';
import { jobRouter } from './job';
import { paymentRouter } from './payment';
import { IDependency } from '../../types/dependency';
import { searchRouter } from './search';

export const routes = (dependencies: IDependency) => {
const router = express.Router();
Expand All @@ -18,6 +19,7 @@ export const routes = (dependencies: IDependency) => {
const membershipPlan = membershipPlanRouter(dependencies);
const job = jobRouter(dependencies);
const payment = paymentRouter(dependencies);
const search = searchRouter(dependencies);

// checkCurrentUser extract current user from jwt, if user is present add it to req.currentUser
// In admin service every routes are protected for admin.
Expand All @@ -31,6 +33,7 @@ export const routes = (dependencies: IDependency) => {
router.use('/membership', membershipPlan);
router.use('/job', job);
router.use('/payment', payment);
router.use('/search', search);

return router;
};
1 change: 0 additions & 1 deletion admin/src/frameworks/express/routes/recruiter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export const recruiterRouter = (dependencies: IDependency) => {
const recruiterController = recruiterControllers(dependencies);

router.get('/recruiters/:page/:limit', recruiterController.getAllRecruitersController);
router.get('/recruiters/search/:page/:limit', recruiterController.searchRecruitersController);
router.get('/view-profile/:userId', recruiterController.getRecruiterByIdController);
router.put('/block-unblock/:userId', recruiterController.recruiterBlockUnblockController);

Expand Down
13 changes: 13 additions & 0 deletions admin/src/frameworks/express/routes/search.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import express, { Router } from 'express';
import { IDependency } from '../../types/dependency';
import { searchControllers } from '../../../controllers';

export const searchRouter = (dependencies: IDependency) => {
const router: Router = express.Router();

const searchController = searchControllers(dependencies);

router.get('/:type/:page/:limit/', searchController.searchController);

return router;
};
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export = {
searchKey: string,
skip: number,
limit: number,
): Promise<ICandidateDocument[] | null> => {
): Promise<ICandidateDocument[] | []> => {

return await CandidateModel.find({
name: {
Expand All @@ -57,7 +57,6 @@ export = {
},

getCountOfSearchedCandidates: async (searchKey: string): Promise<number> => {

return await CandidateModel.countDocuments({ name: { $regex: new RegExp(searchKey, 'i') } });
},
};
32 changes: 24 additions & 8 deletions admin/src/frameworks/repositories/mongo/job.repository.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { JobDocument, JobModel } from '../../database/models';
import { IJobDocument, JobModel } from '../../database/models';
import { IJob } from '../../types/job';

const JOB_SELECT_FIELDS = ['title', 'companyLocation', 'isActive'];
export = {
createJob: async (jobData: IJob): Promise<JobDocument> => {
createJob: async (jobData: IJob): Promise<IJobDocument> => {
const jobObject = JobModel.buildJob(jobData);
return await jobObject.save();
},

blockUnblock: async (jobId: string): Promise<JobDocument> => {
blockUnblock: async (jobId: string): Promise<IJobDocument> => {
const job = await JobModel.findById(jobId);
if (!job) throw new Error('job not found');

Expand All @@ -16,17 +17,17 @@ export = {
return await job.save();
},

updateJob: async (jobId: string, data: Partial<IJob>): Promise<JobDocument | null> => {
updateJob: async (jobId: string, data: Partial<IJob>): Promise<IJobDocument | null> => {
const updatedJob = await JobModel.findOneAndUpdate({ jobId: jobId }, { $set: data }, { new: true });

return updatedJob;
},

getById: async (jobId: string): Promise<JobDocument | null> => {
getById: async (jobId: string): Promise<IJobDocument | null> => {
return await JobModel.findById(jobId);
},

deleteJob: async (jobId: string): Promise<JobDocument | null> => {
deleteJob: async (jobId: string): Promise<IJobDocument | null> => {
const deletedJob = await JobModel.findByIdAndUpdate(
jobId,
{ $set: { isDeleted: true } },
Expand All @@ -37,11 +38,26 @@ export = {
return deletedJob;
},

getAllJobs: async (skip: number, limit: number): Promise<JobDocument[] | []> => {
return await JobModel.find({}).skip(skip).limit(limit);
getAllJobs: async (skip: number, limit: number): Promise<IJobDocument[] | []> => {
return await JobModel.find({}).select(JOB_SELECT_FIELDS).skip(skip).limit(limit);
},

getCountOfJobs: async (): Promise<number> => {
return await JobModel.countDocuments();
},

searchJobs: async (searchKey: string, skip: number, limit: number): Promise<IJobDocument[] | []> => {
return await JobModel.find({
title: {
$regex: new RegExp(searchKey, 'i'),
},
})
.select(JOB_SELECT_FIELDS)
.skip(skip)
.limit(limit);
},

getCountOfSearchedJobs: async (searchKey: string): Promise<number> => {
return await JobModel.countDocuments({ title: { $regex: new RegExp(searchKey, 'i') } });
},
};
18 changes: 17 additions & 1 deletion admin/src/frameworks/repositories/mongo/membership.repository.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { IMembershipPlanData } from '../../../entities/membership-plan';
import { IMembershipPlansDocument, MembershipPlansModel } from '../../database/models';

const MEMBERSHIP_SELECT_FIELDS = ["name", "price","isActive"]

export = {
createMembershipPlan: async (
membershipPlanData: IMembershipPlanData,
Expand Down Expand Up @@ -58,11 +60,25 @@ export = {
const membershipPlans = await MembershipPlansModel.find({})
.skip(skip)
.limit(limit)
.select(['name', 'price']);
.select(MEMBERSHIP_SELECT_FIELDS);
return membershipPlans;
},

getCountOfMembershipPlans: async (): Promise<number> => {
return await MembershipPlansModel.countDocuments();
},

searchMembershipPlans: async (searchKey: string, skip: number, limit: number): Promise<IMembershipPlansDocument[] | []> => {
return await MembershipPlansModel.find({
name: {
$regex: new RegExp(searchKey, 'i'),
},
}).select(MEMBERSHIP_SELECT_FIELDS)
.skip(skip)
.limit(limit);
},

getCountOfSearchedMembershipPlans: async (searchKey: string): Promise<number> => {
return await MembershipPlansModel.countDocuments({ name: { $regex: new RegExp(searchKey, 'i') } });
},
};
119 changes: 106 additions & 13 deletions admin/src/frameworks/repositories/mongo/payment.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,6 @@ export = {
}
},

getAllPayments: async (skip: number, limit: number): Promise<IPaymentDocument[] | []> => {
const payments = await PaymentModel.find({})
.skip(skip)
.limit(limit)
.populate('candidateId', ['name', 'email'])
.populate('membershipPlanId', ['name', 'price']);
return payments;
},

getCountOfPayments: async (): Promise<number> => {
return await PaymentModel.countDocuments();
},

// populate graph data for admin
getGraphData: async () => {
const monthlyPayments = await PaymentModel.aggregate([
Expand Down Expand Up @@ -96,4 +83,110 @@ export = {

return monthlyPayments;
},

getAllPayments: async (skip: number, limit: number): Promise<IPaymentDocument[] | []> => {
const payments = await PaymentModel.find({})
.skip(skip)
.limit(limit)
.populate('candidateId', ['name', 'email'])
.populate('membershipPlanId', ['name', 'price']);
return payments;
},

getCountOfPayments: async (): Promise<number> => {
return await PaymentModel.countDocuments();
},

searchPayments: async (
searchKey: string,
skip: number,
limit: number,
): Promise<IPaymentDocument[] | []> => {
return await PaymentModel.aggregate([
{
$lookup: {
from: 'candidates',
foreignField: '_id',
localField: 'candidateId',
as: 'candidate',
},
},
{
$lookup: {
from: 'membershipplans',
foreignField: '_id',
localField: 'membershipPlanId',
as: 'membershipPlan',
},
},
{
$unwind: {
path: "$candidate",
preserveNullAndEmptyArrays: true
}
},
{
$unwind: {
path: "$membershipPlan",
preserveNullAndEmptyArrays: true
}
},
{
$project: {
candidateId: {
_id: "$candidate._id",
name: "$candidate.name",
email: "$candidate.email",
},
membershipPlanId: {
_id: "$membershipPlan._id",
name: "$membershipPlan.name",
price: "$membershipPlan.price",
},
createdAt: 1,
updatedAt: 1,
},
},
{
$match: {
'candidateId.name': {
$regex: new RegExp(searchKey, 'i'),
},
},
},
{ $skip: skip },
{ $limit: limit },
]);
},

getCountOfSearchedPayments: async (searchKey: string): Promise<number> => {
const result = await PaymentModel.aggregate([
{
$lookup: {
from: 'candidates',
foreignField: '_id',
localField: 'candidateId',
as: 'candidate',
},
},
{
$unwind: {
path: "$candidate",
preserveNullAndEmptyArrays: true
}
},
{
$match: {
'candidate.name': {
$regex: new RegExp(searchKey, 'i'),
},
},
},
{
$count: "totalCount"
}
]);

return result[0]?.totalCount || 0;
},
};
Loading

0 comments on commit 4e0c241

Please sign in to comment.