Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
539baff
Added Id, Name and Address to Form A.3
rithwik-d Apr 17, 2025
fc7648f
Merge branch 'GroupB/development' of https://github.com/IPMS-Project/…
rithwik-d Apr 17, 2025
1c5db44
Updated emailIntegration for Form A.3
rithwik-d Apr 18, 2025
04457fe
Read Only Fields for Internship Advisor Details
lumry Apr 19, 2025
c5f64c9
Read Only Fields for Internship Advisor Details
lumry Apr 19, 2025
93f8b24
Implemented A.3 form reminder system with daily cron job
subhashchandra001 Apr 20, 2025
b7806b0
Read Only Fields for Internship Advisor Details
rithwik-d Apr 20, 2025
48c0619
Merge branch 'GroupB/development' of https://github.com/IPMS-Project/…
rithwik-d Apr 20, 2025
4f47cd5
Sprint 3: Evaluation Submission Flow implemented (Form A.3)
ttabirami12062 Apr 21, 2025
9ddeb57
Updated Evaluation.js schema for Sprint 3: Form A.3 submission flow
ttabirami12062 Apr 21, 2025
e230980
Merged GroupB/development into GroupB/ReminderSystem
subhashchandra001 Apr 21, 2025
a6f349f
Resolve conflict 1
rithwik-d Apr 21, 2025
9b4743a
Resolved Conflicts
rithwik-d Apr 21, 2025
2280da1
Merge branch 'main' into GroupB/Sprint3
rithwik-d Apr 21, 2025
8903b40
Build Error
rithwik-d Apr 21, 2025
5cef0c2
Deleted package.json
rithwik-d Apr 21, 2025
81826e4
Resolved Conflicts in index.js and Evaluation.js
rithwik-d Apr 21, 2025
6ac1a53
Merge branch 'main' into GroupB/Sprint3
rithwik-d Apr 22, 2025
de910d4
Resolved Error
rithwik-d Apr 22, 2025
a5d8cfa
Merge branch 'GroupB/Sprint3' of https://github.com/IPMS-Project/IPMS…
rithwik-d Apr 22, 2025
b530184
Merge remote-tracking branch 'origin/main' into GroupB/Sprint3
rithwik-d Apr 25, 2025
31ab27e
resolved conflicts
rithwik-d Apr 25, 2025
30cde38
Merge branch 'main' into GroupB/Sprint3
subhashchandra001 Apr 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions client/src/pages/A3JobEvaluationForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ const A3JobEvaluationForm = () => {
coordinatorSignature: "",
coordinatorAgreement: false,
});
// const [advisorDetails, setAdvisorDetails] = useState(null);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i guess u are not using this, so remove.

const [errors, setErrors] = useState({});

// Ratings and comments
const [ratings, setRatings] = useState({});
const [comments, setComments] = useState({});
Expand Down Expand Up @@ -172,6 +173,9 @@ const A3JobEvaluationForm = () => {
interneeName: "",
interneeID: "",
interneeEmail: "",
advisorName: "",
advisorJobTitle: "",
advisorEmail: "",
advisorSignature: "",
advisorAgreement: false,
coordinatorSignature: "",
Expand Down Expand Up @@ -231,15 +235,14 @@ const A3JobEvaluationForm = () => {
style={{ backgroundColor: "#fff", maxWidth: "900px", width: "100%" }}
>
<Form onSubmit={handleSubmit}>
<Row className="justify-content-center">
<Row className="justify-content-center" style={{ marginBottom: "20px" }}>
<Col xs={12}>
<div className="border-box">
<h5 style={{ backgroundColor: '#9d2235', color: 'white', padding: '8px', borderRadius: '5px', textAlign: "center", width: '100%',}}>Internee Details</h5>
<Form.Group controlId="interneeName">
<Form.Label>Name</Form.Label>
<Form.Control type="text" value={formData.interneeName} onChange={(e) => handleChange("interneeName", e.target.value)} isInvalid={!!errors.interneeName} placeholder="Enter full name" style={{ maxWidth: "300px" }}/>
<Form.Text className="text-danger">{errors.interneeName}</Form.Text>

</Form.Group>
<Form.Group controlId="interneeID">
<Form.Label>Sooner ID</Form.Label>
Expand All @@ -252,8 +255,24 @@ const A3JobEvaluationForm = () => {
<Form.Text className="text-danger">{errors.interneeEmail}</Form.Text>
</Form.Group>
</div>
<div className="border-box mt-4">
<h5 style={{ backgroundColor: '#9d2235', color: 'white', padding: '8px', borderRadius: '5px', textAlign: "center", width: '100%' }}> Internship Advisor Details </h5>
<Form.Group>
<Form.Label>Name</Form.Label>
<Form.Control type="text" value={formData.advisorName} readOnly style={{ maxWidth: "300px" }} />
</Form.Group>
<Form.Group>
<Form.Label>Job Title</Form.Label>
<Form.Control type="text" value={formData.advisorJobTitle} readOnly style={{ maxWidth: "300px" }} />
</Form.Group>
<Form.Group>
<Form.Label>Email</Form.Label>
<Form.Control type="email" value={formData.advisorEmail} readOnly style={{ maxWidth: "300px" }} />
</Form.Group>
</div>
</Col>
</Row>

<Table bordered responsive className="text-center custom-table">
<thead>
<tr>
Expand Down
15 changes: 8 additions & 7 deletions server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ const fourWeekReportRoutes = require("./routes/fourWeekReportRoutes");
const path = require("path");



const cronJobRoutes = require("./routes/cronJobRoutes");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

u are not using these imports anywhere in the file. so remove.


// Author Subhash Chandra: Form A3 Reminder Job Logic
const { registerReminderA3Job } = require("./utils/reminderA3Utils");

const app = express();
app.use(express.json());
app.use(cors());
Expand Down Expand Up @@ -94,9 +100,7 @@ app.post("/api/createUser", async (req, res) => {
res.status(201).json({ message: "User created successfully", user });
} catch (error) {
console.error("Error creating user:", error);
res
.status(500)
.json({ message: "Failed to create user", error: error.message });
res.status(500).json({ message: "Failed to create user", error: error.message });
}
});

Expand Down Expand Up @@ -139,9 +143,6 @@ app.post("/api/evaluation", async (req, res) => {
}
});




//Form A.4
const presentationRoutes = require("./routes/presentationRoutes");
app.use("/api/presentation", presentationRoutes);
Expand All @@ -159,4 +160,4 @@ process.on("SIGINT", async () => {
});

const PORT = process.env.PORT || 5001;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
13 changes: 12 additions & 1 deletion server/jobs/cronJobsConfig.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
const CronJob = require("../models/CronJob");
const { coordinatorReminder, supervisorReminder } = require("./reminderEmail");

// Import ALL required reminder jobs from all branches
const {
coordinatorReminder,
supervisorReminder,
evaluationReminder,
} = require("./reminderEmail");

const { checkAndSendReminders } = require("./tokenExpiryCheck");
const autoDeactivateCronjobs = require("./autoDeactivateCronjobs");

Expand All @@ -26,6 +33,10 @@ async function getCronJobs() {
schedule: job.schedule,
job: async () => {
try {
await CronJob.findByIdAndUpdate(job._id, {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

repeated twice

lastRun: new Date(),
});
await jobFunctions[job.name]();
// Update last execution time
await CronJob.findByIdAndUpdate(job._id, {
lastRun: new Date(),
Expand Down
40 changes: 38 additions & 2 deletions server/jobs/reminderEmail.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,43 @@ const supervisorReminder = async () => {
}
};

const Evaluation = require("../models/Evaluation");

const evaluationReminder = async () => {
try {
const pendingEvals = await Evaluation.find({
evaluations: { $exists: false },
advisorAgreement: true,
});

for (const evalDoc of pendingEvals) {
const emailHtml = `
<p>Dear Supervisor,</p>
<p>This is a reminder to complete the <strong>Final Job Performance Evaluation (Form A.3)</strong> for:</p>
<ul>
<li><strong>Name:</strong> ${evalDoc.interneeName}</li>
<li><strong>Sooner ID:</strong> ${evalDoc.interneeID}</li>
</ul>
<p>The deadline is approaching. Please use the link below to complete the evaluation:</p>
<p><a href="${process.env.CLIENT_URL}/evaluation/${evalDoc._id}">Complete A.3 Evaluation</a></p>
<p>Thanks,<br/>IPMS Team</p>
`;

await emailService.sendEmail({
to: evalDoc.interneeEmail, // or supervisor's email if you have it
subject: "Reminder: Pending A.3 Evaluation Submission",
html: emailHtml,
});

console.log(`✅ Reminder sent for: ${evalDoc.interneeName}`);
}
} catch (error) {
console.error("❌ Error sending A.3 reminders:", error);
}
};

module.exports = {
coordinatorReminder,
supervisorReminder,
coordinatorReminder,
supervisorReminder,
evaluationReminder,
};
79 changes: 65 additions & 14 deletions server/models/Evaluation.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,54 @@
const mongoose = require('mongoose');
const formMetadata = require('./FormMetadata');

// Signature schema for both advisor and coordinator
const signatureSchema = new mongoose.Schema({
type: { type: String, enum: ['text', 'draw'], required: true },
value: { type: String, required: true },
font: { type: String }
type: {
type: String,
enum: ['text', 'draw'],
required: true
},
value: {
type: String,
required: true
},
font: {
type: String // used only if type is 'text'
}
}, { _id: false });

// Individual evaluation item schema
const evaluationItemSchema = new mongoose.Schema({
category: {
type: String,
required: true,
required: true
},
rating: {
type: String,
enum: ['Satisfactory', 'Unsatisfactory'],
required: true
},
comment: { type: String, maxlength: 500 }
comment: {
type: String,
maxlength: 500
}
}, { _id: false });

// Main evaluation schema
const evaluationSchema = new mongoose.Schema({
...formMetadata,
interneeId: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: false },
internshipId: { type: mongoose.Schema.Types.ObjectId, ref: 'Internship', required: false },

interneeId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: false
},

internshipId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Internship',
required: false
},

interneeName: {
type: String,
Expand All @@ -36,13 +61,13 @@ const evaluationSchema = new mongoose.Schema({
interneeID: {
type: String,
required: true,
match: [/^\d{9}$/, 'Sooner ID must be a 9-digit number'] // Sooner ID validation
match: [/^\d{9}$/, 'Sooner ID must be a 9-digit number']
},

interneeEmail: {
type: String,
required: true,
match: [/\S+@\S+\.\S+/, 'Invalid email format'], // Email format validation
match: [/\S+@\S+\.\S+/, 'Invalid email format'],
lowercase: true,
trim: true
},
Expand All @@ -52,13 +77,39 @@ const evaluationSchema = new mongoose.Schema({
validate: [arr => arr.length > 0, 'At least one evaluation item is required']
},

advisorSignature: { type: signatureSchema, required: true },
advisorAgreement: { type: Boolean, required: true },
coordinatorSignature: { type: signatureSchema, required: true },
coordinatorAgreement: { type: Boolean, required: true }
advisorSignature: {
type: signatureSchema,
required: true
},

advisorAgreement: {
type: Boolean,
required: true
},

coordinatorSignature: {
type: signatureSchema,
required: true
},

coordinatorAgreement: {
type: Boolean,
required: true
},

status: {
type: String,
enum: ['draft', 'submitted'],
default: 'draft'
},

submittedAt: {
type: Date
}

}, { timestamps: true });

// Unique index to prevent duplicate submissions
evaluationSchema.index({ interneeID: 1, internshipId: 1 });

module.exports = mongoose.model('Evaluation', evaluationSchema);
module.exports = mongoose.model('Evaluation', evaluationSchema);
43 changes: 43 additions & 0 deletions server/routes/evaluationRoutes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const express = require('express');
const router = express.Router();
const Evaluation = require('../models/Evaluation');

// POST: Submit Evaluation Form A.3
router.post('/submit', async (req, res) => {
try {
const data = req.body;

// Basic validation: required fields check
if (
!data.interneeName ||
!data.interneeID ||
!data.interneeEmail ||
!data.evaluations ||
data.evaluations.length !== 3
) {
return res.status(400).json({ message: 'Missing required fields or invalid number of evaluations' });
}

const evaluation = new Evaluation({
interneeName: data.interneeName,
interneeID: data.interneeID,
interneeEmail: data.interneeEmail,
evaluations: data.evaluations,
advisorSignature: data.advisorSignature,
advisorAgreement: data.advisorAgreement,
coordinatorSignature: data.coordinatorSignature,
coordinatorAgreement: data.coordinatorAgreement,
status: 'submitted',
submittedAt: new Date()
});

await evaluation.save();
res.status(201).json({ message: 'Evaluation submitted successfully' });

} catch (err) {
console.error('Error submitting evaluation:', err);
res.status(500).json({ error: 'Internal server error' });
}
});

module.exports = router;
Loading