Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
115 changes: 57 additions & 58 deletions client/src/pages/SupervisorDashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,25 @@ const SupervisorDashboard = () => {
useEffect(() => {
const fetchRequests = async () => {
try {
const res = await axios.get(`${process.env.REACT_APP_API_URL}/api/submissions/pending`);

setRequests(res.data);
const res = await axios.get(`${process.env.REACT_APP_API_URL}/api/form/internshiprequests`);
const formatted = res.data.map(item => ({
_id: item._id,
name: item.workplace?.name || "N/A",
student_id: item._id, // display _id of InternshipRequest
form_type: "A1",
createdAt: item.createdAt,
supervisor_status: "pending",
fullForm: item
}));
setRequests(formatted);
setLoading(false);
} catch (err) {
console.error("Error fetching requests:", err);
setMessage("Error fetching requests.");
console.error("Error fetching Internship A1 forms:", err);
setMessage("Error fetching Internship A1 forms.");
setLoading(false);
}
};

fetchRequests();
}, []);

Expand All @@ -30,7 +39,7 @@ const SupervisorDashboard = () => {
if (!confirmed) return;

try {
const res = await axios.post(`${process.env.REACT_APP_API_URL}/api/submissions/${id}/${action}`, { comment });
const res = await axios.post(`${process.env.REACT_APP_API_URL}/api/form/internshiprequests/${id}/${action}`, { comment });

setMessage(res.data.message || `${action} successful`);
setRequests(prev => prev.filter(req => req._id !== id));
Expand All @@ -44,62 +53,52 @@ const SupervisorDashboard = () => {
const openFormView = (form) => setSelectedForm(form);
const closeFormView = () => setSelectedForm(null);

const formatDate = (dateStr) => new Date(dateStr).toLocaleDateString();

const sortedRequests = [...requests].sort((a, b) => new Date(a.createdAt) - new Date(b.createdAt));

let content;

if (loading) {
content = <p>Loading...</p>;
}
else if (sortedRequests.length === 0) {
content = (
<div className="empty-message-container">
<div className="empty-message">No pending approvals.</div>
</div>
);
}
else {
content = (
<table className="dashboard-table">
<thead>
<tr>
<th>Student Name</th>
<th>Student ID</th>
<th>Form Type</th>
<th>Date Submitted</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{sortedRequests.map((req) => (
<tr key={req._id}>
<td>{req.name}</td>
<td>
<button className="link-button" onClick={() => openFormView(req)}>
{req.student_id}
</button>
</td>
<td>{req.form_type}</td>
<td>{formatDate(req.createdAt)}</td>
<td>
<span className={`status-badge ${req.supervisor_status}`}>
{req.supervisor_status}
</span>
</td>
</tr>
))}
</tbody>
</table>
);
}
const formatDate = (date) => new Date(date).toLocaleDateString();

return (
<div className="dashboard-container">
<h2>Supervisor Dashboard</h2>
{message && <p className="status-msg">{message}</p>}
{content}

{loading ? (
<p>Loading...</p>
) : requests.length === 0 ? (
<div className="empty-message-container">
<div className="empty-message">No pending approvals.</div>
</div>
) : (
<table className="dashboard-table">
<thead>
<tr>
<th>Student Name</th>
<th>Student ID</th>
<th>Form Type</th>
<th>Date Submitted</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{requests.map((req) => (
<tr key={req._id}>
<td>{req.name}</td>
<td>
<button className="link-button" onClick={() => openFormView(req.fullForm)}>
{req.student_id}
</button>
</td>
<td>{req.form_type}</td>
<td>{formatDate(req.createdAt)}</td>
<td>
<span className={`status-badge ${req.supervisor_status}`}>
{req.supervisor_status}
</span>
</td>
</tr>
))}
</tbody>
</table>
)}

{selectedForm && (
<ViewFormModal
formData={selectedForm}
Expand All @@ -111,4 +110,4 @@ const SupervisorDashboard = () => {
);
};

export default SupervisorDashboard;
export default SupervisorDashboard;
44 changes: 22 additions & 22 deletions client/src/pages/ViewFormModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React, { useState } from "react";
import "../styles/SupervisorDashboard.css";

const ViewFormModal = ({ formData, onClose, onAction }) => {
const form = typeof formData.details === "string" ? JSON.parse(formData.details) : formData.details;
const [comment, setComment] = useState("");
const [error, setError] = useState("");

Expand All @@ -11,32 +10,33 @@ const ViewFormModal = ({ formData, onClose, onAction }) => {
setError("Comment is required before taking action.");
return;
}
setError(""); // clear error
setError("");
onAction(formData._id, action, comment);
};

return (
<div className="modal-overlay">
<div className="modal-box">
<h2>Form: {formData.form_type}</h2>
<p><strong>Student:</strong> {formData.name}</p>
<h2>Form: A1</h2>
<p><strong>Student ID:</strong> {formData.student}</p>
<p><strong>Workplace:</strong> {formData.workplace?.name}</p>
<p><strong>Advisor:</strong> {formData.internshipAdvisor?.name}</p>
<p><strong>Credit Hours:</strong> {formData.creditHours}</p>
<p><strong>Start Date:</strong> {new Date(formData.startDate).toLocaleDateString()}</p>
<p><strong>End Date:</strong> {new Date(formData.endDate).toLocaleDateString()}</p>

{form.tasks && (
<div>
<strong>Tasks:</strong>
<ul>{form.tasks.map((task, i) => <li key={i}>{task}</li>)}</ul>
</div>
)}

{form.outcomes && (
<div>
<strong>Outcomes:</strong>
<ul>{form.outcomes.map((o, i) => <li key={i}>{o}</li>)}</ul>
</div>
)}

{form.week && <p><strong>Week:</strong> {form.week}</p>}
{form.lessonsLearned && <p><strong>Lessons Learned:</strong> {form.lessonsLearned}</p>}
<div>
<strong>Tasks:</strong>
<ul>
{formData.tasks?.map((task, index) => (
<li key={index}>
<strong>Description:</strong> {task.description}
<br />
<strong>Outcomes:</strong> {task.outcomes.join(", ")}
</li>
))}
</ul>
</div>

<div>
<label><strong>Comment:</strong></label>
Expand All @@ -47,10 +47,10 @@ const ViewFormModal = ({ formData, onClose, onAction }) => {
rows={4}
style={{ width: "100%", marginTop: "5px", borderRadius: "4px", padding: "8px" }}
/>
{error && <p style={{ color: "red", marginTop: "5px" }}>{error}</p>}
{error && <p style={{ color: "red" }}>{error}</p>}
</div>

<div style={{ marginTop: "15px", display: "flex", gap: "10px", justifyContent: "center" }}>
<div style={{ display: "flex", justifyContent: "center", gap: "10px", marginTop: "10px" }}>
<button className="approve" onClick={() => handleDecision("approve")}>Approve</button>
<button className="reject" onClick={() => handleDecision("reject")}>Reject</button>
</div>
Expand Down
44 changes: 33 additions & 11 deletions server/controllers/approvalController.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 });
}
Expand All @@ -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 });
}
Expand All @@ -63,19 +77,22 @@ 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) {
res.status(500).json({ message: "Failed to fetch details" });
}
};

// Coordinator Approve Request
exports.coordinatorApproveRequest = async (req, res) => {
try {
const request = await InternshipRequest.findByIdAndUpdate(
Expand All @@ -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,
Expand All @@ -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" });
Expand All @@ -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,
Expand All @@ -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 });
}
};
Loading