Skip to content

Commit 328dfce

Browse files
authored
Implemented invitation functionality (#149)
1 parent 6e45d84 commit 328dfce

File tree

8 files changed

+178
-77
lines changed

8 files changed

+178
-77
lines changed

src/models/AuthUser.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ const userSchema = new Schema(
3939
},
4040
applicationPhase: {
4141
type: String,
42-
enum: ["Applied", "Interviewed", "Accepted", "Enrolled"],
42+
enum: ["Applied", 'Shortlisted', 'Technical Assessment', 'Interview Assessment', 'Admitted', 'Rejected', "Enrolled"],
4343
default: "Applied",
4444
},
4545
isActive: {

src/models/ShortlistedSchema.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import mongoose, { Schema, Document } from "mongoose";
22

33
interface IShortlisted extends Document {
44
applicantId: mongoose.Schema.Types.ObjectId;
5-
status: "No action" | "Moved" | "Rejected" | "Admitted";
5+
status: "No action" | "Invited" | "Moved" | "Rejected" | "Admitted";
66
comments?: string;
77
}
88

@@ -15,7 +15,7 @@ const shortlistedSchema = new Schema<IShortlisted>({
1515
},
1616
status: {
1717
type: String,
18-
enum: ["No action", "Moved", "Rejected", "Admitted"],
18+
enum: ["No action", "Invited", "Moved", "Rejected", "Admitted"],
1919
default: "No action",
2020
},
2121
comments: {

src/models/technicalAssessmentStage.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ import mongoose, { Schema, Document } from "mongoose";
22

33
interface ITechnicalAssessment extends Document {
44
applicantId: mongoose.Schema.Types.ObjectId;
5-
status: "No action" | "Moved" | "Rejected" | "Admitted";
5+
status: "No action" | "Invited"| "Moved" | "Rejected" | "Admitted";
66
score: number;
7+
invitationLink: string;
8+
platform:string;
79
comments?: string;
810
}
911

@@ -15,7 +17,7 @@ const technicalAssessmentSchema = new Schema<ITechnicalAssessment>({
1517
},
1618
status: {
1719
type: String,
18-
enum: ["No action", "Moved", "Rejected", "Admitted"],
20+
enum: ["No action", "Invited", "Moved", "Rejected", "Admitted"],
1921
default: "No action",
2022
},
2123
score: {
@@ -24,6 +26,12 @@ const technicalAssessmentSchema = new Schema<ITechnicalAssessment>({
2426
max: 100,
2527
default:null
2628
},
29+
platform:{
30+
type:String,
31+
},
32+
invitationLink:{
33+
type:String,
34+
},
2735
comments: {
2836
type: String,
2937
},

src/resolvers/Doc.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export const docResolver:any={
2727
return doc;
2828
},
2929
async deleteDoc(_:any,args:any){
30-
const doc= await docModels.findOneAndDelete({id:args.id})
30+
const doc= await docModels.findOneAndDelete({_id:args.id})
3131
return doc;
3232
},
3333
async updateDoc(_:any,args:any){

src/resolvers/applicationStageResolver.ts

Lines changed: 97 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ const models = [
3131
async function getApplicantsByModel(model: any) {
3232
return await model.find().populate("applicantId").exec();
3333
}
34-
async function updateApplicantAfterDismissed(model: any, applicantId: string) {
34+
async function updateApplicantAfterRejected(model: any, applicantId: string) {
3535
await model.updateOne({ applicantId }, { $set: { status: "Rejected" } });
3636
}
37-
async function updateApplicantAfterAdmitted(model: any,applicantId:string) {
37+
async function updateApplicantAfterAdmitted(model: any, applicantId: string) {
3838
await model.updateOne({ applicantId }, { $set: { status: "Admitted" } });
3939
}
4040
export const applicationStageResolvers: any = {
@@ -85,6 +85,8 @@ export const applicationStageResolvers: any = {
8585
status: tracking.status,
8686
score: tracking.score || tracking.interviewScore,
8787
comments: tracking.comments,
88+
platform: tracking.platform,
89+
invitationLink: tracking.invitationLink,
8890
createdAt: tracking.createdAt.toLocaleString(),
8991
updatedAt: tracking.updatedAt.toLocaleString(),
9092
}));
@@ -148,7 +150,7 @@ export const applicationStageResolvers: any = {
148150
technicalStage,
149151
interviewStage,
150152
admittedStage,
151-
dismissedStage,
153+
rejectedStage,
152154
AllStages,
153155
] = await Promise.all([
154156
Shortlisted.findOne({ applicantId: trainee }),
@@ -164,7 +166,7 @@ export const applicationStageResolvers: any = {
164166
technical: technicalStage,
165167
interview: interviewStage,
166168
admitted: admittedStage,
167-
dismissed: dismissedStage,
169+
rejected: rejectedStage,
168170
allStages: AllStages,
169171
};
170172
} catch (error) {
@@ -301,7 +303,7 @@ export const applicationStageResolvers: any = {
301303
await TraineeApplicant.updateOne(
302304
{ _id: applicantId },
303305
{ $set: { applicationPhase: nextStage, status: "No action" } }
304-
);
306+
);
305307
message = `You have advanced to the ${nextStage} stage.`;
306308
const notification = await ApplicantNotificationsModel.create({
307309
userId: user!._id,
@@ -477,23 +479,23 @@ export const applicationStageResolvers: any = {
477479
}
478480
await Promise.all(
479481
models.map((model) =>
480-
updateApplicantAfterDismissed(model, applicantId)
482+
updateApplicantAfterRejected(model, applicantId)
481483
)
482484
);
483-
const stageDismissedFrom = await TraineeApplicant.findOne({
485+
const stageRejectedFrom = await TraineeApplicant.findOne({
484486
_id: applicantId,
485487
});
486488
await Rejected.create({
487489
applicantId,
488-
stageDismissedFrom: stageDismissedFrom?.applicationPhase,
490+
stageRejectedFrom: stageRejectedFrom?.applicationPhase,
489491
comments,
490492
});
491493
await TraineeApplicant.updateOne(
492494
{ _id: applicantId },
493495
{ $set: { applicationPhase: "Rejected", status: "Rejected" } }
494496
);
495-
message = `You have been rejected from the ${stageDismissedFrom?.applicationPhase} stage.`;
496-
497+
message = `You have been rejected from the ${stageRejectedFrom?.applicationPhase} stage.`;
498+
497499
const notification3 = await ApplicantNotificationsModel.create({
498500
userId: user!._id,
499501
message,
@@ -505,7 +507,7 @@ export const applicationStageResolvers: any = {
505507
"Application Update",
506508
`Hello ${user!.email.split("@")[0]}, `,
507509
`We are sorry to inform you that
508-
your application has been rejected from the ${stageDismissedFrom?.applicationPhase} stage.
510+
your application has been rejected from the ${stageRejectedFrom?.applicationPhase} stage.
509511
<br />
510512
<br />
511513
You can always apply again.
@@ -639,5 +641,89 @@ export const applicationStageResolvers: any = {
639641
return new Error(error.message);
640642
}
641643
},
644+
sendInvitation: async ( _: any, { applicantId, email, platform,invitationLink,}: { applicantId: string; email: string; platform: string; invitationLink: string;}, context: any) => {
645+
try {
646+
if (!context.currentUser) {
647+
throw new CustomGraphQLError(
648+
"You must be logged in to perform this action."
649+
);
650+
}
651+
652+
if (!email || !invitationLink) {
653+
throw new CustomGraphQLError(
654+
"Email and invitation link are required."
655+
);
656+
}
657+
658+
// Find the applicant
659+
const isApplicantExist = await TechnicalAssessment.findOne({
660+
applicantId,
661+
})
662+
.populate("applicantId")
663+
.exec();
664+
665+
if (!isApplicantExist || !isApplicantExist.applicantId) {
666+
throw new Error("Applicant not found or applicantId is missing.");
667+
}
668+
669+
const user = await LoggedUserModel.findOne({ email });
670+
const applicant = isApplicantExist.applicantId as any;
671+
const firstName = applicant.firstName;
672+
const lastName = applicant.lastName;
673+
const notification = await ApplicantNotificationsModel.create({
674+
userId: user!._id,
675+
message:"Invitation link has sent to your email address. Please check your email address",
676+
eventType: "general",
677+
});
678+
await pusher
679+
.trigger(`notifications-${user!._id}`, "new-notification", {
680+
message: notification.message,
681+
id: notification._id,
682+
createdAt: notification.createdAt,
683+
read: notification.read,
684+
})
685+
.catch((error) => {
686+
console.error("Error with Pusher trigger:", error);
687+
});
688+
await sendEmailTemplate(
689+
email,
690+
"Invitation to Complete Technical Assessment",
691+
`Dear ${firstName} ${lastName},`,
692+
` <p>
693+
We are excited to invite you to take the next step in your application process! <br>
694+
Please complete the following technical assessment to continue:<br>
695+
<a href="${invitationLink}" target="_blank">${invitationLink}</a><br>
696+
The assessment will be hosted on the <strong>${platform}</strong> platform. Please ensure that you have the necessary access and requirements ready.
697+
</p>
698+
<p>
699+
Once you've finished the assessment, we'll review your results and follow up with the next steps.
700+
</p>
701+
`,
702+
{
703+
text: "Invitation link",
704+
url: invitationLink,
705+
}
706+
);
707+
708+
await TraineeApplicant.updateOne(
709+
{ _id: applicantId },
710+
{ $set: { status: "Invited" } }
711+
);
712+
await TechnicalAssessment.updateOne(
713+
{ applicantId },
714+
{ $set: { status: "Invited" ,invitationLink, platform} }
715+
);
716+
717+
return {
718+
success: true,
719+
message: "Invitation sent successfully",
720+
};
721+
} catch (error: any) {
722+
return {
723+
success: false,
724+
message: error.message,
725+
};
726+
}
727+
},
642728
},
643729
};

0 commit comments

Comments
 (0)