+
diff --git a/server/controllers/approvalController.js b/server/controllers/approvalController.js
index 32582466..4627f70b 100644
--- a/server/controllers/approvalController.js
+++ b/server/controllers/approvalController.js
@@ -19,15 +19,22 @@ exports.getPendingSubmissions = async (req, res) => {
exports.approveSubmission = async (req, res) => {
const { id } = req.params;
const { comment } = req.body;
+
try {
- const submission = await Submission.findByIdAndUpdate(
+ const submission = await InternshipRequest.findByIdAndUpdate(
id,
{ supervisor_status: "Approved", supervisor_comment: comment || "" },
{ new: true }
);
- if (!submission)
+
+ if (!submission) {
return res.status(404).json({ message: "Submission not found" });
- res.json({ message: "Submission Approved", updatedSubmission: submission });
+ }
+
+ res.json({
+ message: "Submission approved and forwarded to Coordinator",
+ updatedSubmission: submission,
+ });
} catch (err) {
res.status(500).json({ message: "Approval Failed", error: err });
}
@@ -37,15 +44,22 @@ exports.approveSubmission = async (req, res) => {
exports.rejectSubmission = async (req, res) => {
const { id } = req.params;
const { comment } = req.body;
+
try {
- const submission = await Submission.findByIdAndUpdate(
+ const submission = await InternshipRequest.findByIdAndUpdate(
id,
{ supervisor_status: "Rejected", supervisor_comment: comment || "" },
{ new: true }
);
- if (!submission)
+
+ if (!submission) {
return res.status(404).json({ message: "Submission not found" });
- res.json({ message: "Submission Rejected", updatedSubmission: submission });
+ }
+
+ res.json({
+ message: "Submission rejected and sent back to student",
+ updatedSubmission: submission,
+ });
} catch (err) {
res.status(500).json({ message: "Rejection Failed", error: err });
}
@@ -63,12 +77,14 @@ exports.getCoordinatorRequests = async (req, res) => {
}
};
+// Coordinator View Single Request
exports.getCoordinatorRequestDetails = async (req, res) => {
try {
const requestData = await InternshipRequest.findById(req.params.id).lean();
- if (!requestData)
+ if (!requestData) {
return res.status(404).json({ message: "Request not found" });
+ }
res.status(200).json({ requestData, supervisorStatus: "Not Submitted" });
} catch (err) {
@@ -76,6 +92,7 @@ exports.getCoordinatorRequestDetails = async (req, res) => {
}
};
+// Coordinator Approve Request
exports.coordinatorApproveRequest = async (req, res) => {
try {
const request = await InternshipRequest.findByIdAndUpdate(
@@ -84,7 +101,9 @@ exports.coordinatorApproveRequest = async (req, res) => {
{ new: true }
);
- if (!request) return res.status(404).json({ message: "Request not found" });
+ if (!request) {
+ return res.status(404).json({ message: "Request not found" });
+ }
await EmailService.sendEmail({
to: request.student.email,
@@ -94,10 +113,11 @@ exports.coordinatorApproveRequest = async (req, res) => {
res.json({ message: "Request Approved Successfully" });
} catch (err) {
- res.status(500).json({ message: "Approval failed" });
+ res.status(500).json({ message: "Approval failed", error: err.message });
}
};
+// Coordinator Reject Request
exports.coordinatorRejectRequest = async (req, res) => {
const { reason } = req.body;
if (!reason) return res.status(400).json({ message: "Reason required" });
@@ -109,7 +129,9 @@ exports.coordinatorRejectRequest = async (req, res) => {
{ new: true }
);
- if (!request) return res.status(404).json({ message: "Request not found" });
+ if (!request) {
+ return res.status(404).json({ message: "Request not found" });
+ }
await EmailService.sendEmail({
to: request.student.email,
@@ -119,6 +141,6 @@ exports.coordinatorRejectRequest = async (req, res) => {
res.json({ message: "Request Rejected Successfully" });
} catch (err) {
- res.status(500).json({ message: "Rejection failed" });
+ res.status(500).json({ message: "Rejection failed", error: err.message });
}
};
diff --git a/server/models/InternshipRequest.js b/server/models/InternshipRequest.js
index 3732f04d..91fb8936 100644
--- a/server/models/InternshipRequest.js
+++ b/server/models/InternshipRequest.js
@@ -2,71 +2,89 @@ const mongoose = require("mongoose"); // why are we commonjs
const ObjectId = mongoose.Schema.Types.ObjectId;
const Task = new mongoose.Schema({
- _id: false,
- description: {
- type: String,
- required: true
- },
- outcomes: {
- type: [String],
- enum: ['problemSolving','solutionDevelopment', 'communication', 'decisionMaking', 'collaboration', 'application']
- }
+ _id: false,
+ description: {
+ type: String,
+ required: true,
+ },
+ outcomes: {
+ type: [String],
+ enum: [
+ "problemSolving",
+ "solutionDevelopment",
+ "communication",
+ "decisionMaking",
+ "collaboration",
+ "application",
+ ],
+ },
});
-const formA1 = new mongoose.Schema({
- student: { // get student's name, email, id from User
- type: ObjectId,
- required: true,
- ref: 'User'
+const formA1 = new mongoose.Schema(
+ {
+ student: {
+ // get student's name, email, id from User
+ type: ObjectId,
+ required: true,
+ ref: "User",
},
workplace: {
- name: {
- type: String,
- required: true,
- },
- website: String,
- phone: String, // TODO how to validate this?
+ name: {
+ type: String,
+ required: true,
+ },
+ website: String,
+ phone: String, // TODO how to validate this?
},
internshipAdvisor: {
- name: String,
- jobTitle: String,
- email: {
- type: String,
- required: true
- }
+ name: String,
+ jobTitle: String,
+ email: {
+ type: String,
+ required: true,
+ },
},
creditHours: {
- type: Number,
- required: true,
- enum: [1, 2, 3]
+ type: Number,
+ required: true,
+ enum: [1, 2, 3],
},
startDate: {
- type: Date,
- required: true
+ type: Date,
+ required: true,
},
- endDate: { // TODO how to make sure endDate is later than startDate?
- type: Date,
- required: true
+ endDate: {
+ // TODO how to make sure endDate is later than startDate?
+ type: Date,
+ required: true,
},
tasks: {
- type: [Task],
- required: true
+ type: [Task],
+ required: true,
},
status: {
- type: String,
- required: true,
- enum: ['draft', 'submitted','pending manual review' ,'approved']
+ type: String,
+ required: true,
+ enum: ["draft", "submitted", "pending manual review", "approved"],
+ },
+ supervisor_status: {
+ type: String,
+ },
+ supervisor_comment: {
+ type: String,
},
approvals: {
- type: [String],
- enum: ['advisor', 'coordinator']
+ type: [String],
+ enum: ["advisor", "coordinator"],
},
reminders: [Date],
// requiredHours is an easily derived attribute
// TODO needs to be a virtual getter that checks this student's WeeklyReports
- completedHours: Number
-}, { timestamps: true });
-formA1.virtual("requiredHours").get(function() {
- return this.creditHours * 60;
-})
+ completedHours: Number,
+ },
+ { timestamps: true }
+);
+formA1.virtual("requiredHours").get(function () {
+ return this.creditHours * 60;
+});
-module.exports = mongoose.model("InternshipRequest", formA1);
\ No newline at end of file
+module.exports = mongoose.model("InternshipRequest", formA1);
diff --git a/server/routes/formRoutes.js b/server/routes/formRoutes.js
index c80f3ebe..3ed75f52 100644
--- a/server/routes/formRoutes.js
+++ b/server/routes/formRoutes.js
@@ -1,72 +1,81 @@
-const express = require('express');
+const express = require("express");
const router = express.Router();
-const { insertFormData } = require('../services/insertData');
+const InternshipRequest = require("../models/InternshipRequest");
+const { insertFormData } = require("../services/insertData");
+const {
+ getPendingSubmissions,
+ approveSubmission,
+ rejectSubmission
+} = require("../controllers/approvalController");
-let status = '';
+router.post("/internshiprequests/:id/approve", approveSubmission);
+router.post("/internshiprequests/:id/reject", rejectSubmission);
-// Validate required fields
+// GET route to fetch internship requests without supervisor_comment and supervisor_status
+router.get("/internshiprequests", async (req, res) => {
+ try {
+ const requests = await InternshipRequest.find({
+ status: "submitted",
+ approvals: { $all: ["advisor", "coordinator"] },
+ supervisor_comment: { $exists: false },
+ supervisor_status: { $exists: false }
+ }).sort({ createdAt: -1 });
+
+ res.status(200).json(requests);
+ } catch (err) {
+ console.error("Error fetching internship requests:", err);
+ res.status(500).json({ message: "Server error while fetching internship requests" });
+ }
+});
+
+
+// Validate and submit form
function validateFormData(formData) {
const requiredFields = [
- 'workplaceName',
- 'website',
- 'phone',
- 'advisorName',
- 'advisorJobTitle',
- 'advisorEmail',
- 'creditHours',
- 'startDate',
- 'endDate',
- 'tasks'
+ "workplaceName",
+ "website",
+ "phone",
+ "advisorName",
+ "advisorJobTitle",
+ "advisorEmail",
+ "creditHours",
+ "startDate",
+ "endDate",
+ "tasks"
];
for (const field of requiredFields) {
- if (!formData[field] || formData[field] === '') {
+ if (!formData[field] || formData[field] === "") {
return `Missing or empty required field: ${field}`;
}
}
if (!Array.isArray(formData.tasks) || formData.tasks.length === 0) {
- return 'Tasks must be a non-empty array';
+ return "Tasks must be a non-empty array";
}
- // for (const [index, task] of formData.tasks.entries()) {
- // if (!task.description || !task.outcomes) {
- // return `Task at index ${index} is missing description or outcomes`;
- // }
- // }
-
- // uncomment below if student has to fill in task outcomes
- // const filledTasks = formData.tasks.filter((task) => task.description && task.outcomes );
- // if (filledTasks.length < 3)
- // return `At least 3 tasks must have description and outcomes; only ${filledTasks.length} do`;
- const tasks = formData.tasks;
- console.log(tasks);
- if (tasks.filter((task) => task.description).length < 3)
- return 'At least 3 tasks must be provided';
- const uniqueOutcomes = new Set();
- tasks.forEach((task) => {
- if (Array.isArray(task.outcomes)) {
- task.outcomes.forEach(outcome => uniqueOutcomes.add(outcome));
- }
+ const outcomes = new Set();
+ formData.tasks.forEach((task) => {
+ task.outcomes?.forEach(o => outcomes.add(o));
});
- formData.status = uniqueOutcomes.size < 3 ? 'pending manual review' : 'submitted';
+
+ formData.status = outcomes.size < 3 ? "pending manual review" : "submitted";
return null;
}
-router.post('/submit', async (req, res) => {
+router.post("/submit", async (req, res) => {
const formData = req.body;
- const validationError = validateFormData(formData);
- if (validationError) {
- return res.status(400).json({ message: validationError });
- }
+ const error = validateFormData(formData);
+ if (error) return res.status(400).json({ message: error });
try {
await insertFormData(formData);
- res.status(200).json({ message: 'Form received and handled!', status, manual: formData.status !== 'submitted'});
+ res.status(200).json({ message: "Form received and stored." });
} catch (error) {
- console.error('Error handling form data:', error);
- res.status(500).json({ message: 'Something went wrong' });
+ console.error("Insert error:", error);
+ res.status(500).json({ message: "Something went wrong" });
}
});
+
module.exports = router;