Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
712c2ae
Refactor: Integrated supervisor reminder mail using cron jobs
vikashbalajik Apr 14, 2025
aea0a18
Revert "Group d/vikash sprint2"
vikashbalajik Apr 19, 2025
5486ded
Revert "Group d/vikash sprint2"
vikashbalajik Apr 14, 2025
52ceb76
Update cronUtils.test.js
vikashbalajik Apr 14, 2025
899380f
Update cronUtils.test.js
vikashbalajik Apr 14, 2025
3abc6b2
Fix: Integrated cron jobs & API routes properly for 4-week report
vikashbalajik Apr 14, 2025
acd2eb5
sprint 2 another way
vikashbalajik Apr 15, 2025
370c3a4
✅ Final: show supervisor comments in read-only weekly report
vikashbalajik Apr 19, 2025
388b88f
sprint 3
vikashbalajik Apr 21, 2025
95d2007
Merge branch 'main' of https://github.com/IPMS-Project/IPMS into grou…
RohanMukka Apr 21, 2025
21f01ce
Update reminderEmail.js
vikashbalajik Apr 21, 2025
1857e69
resolved conflicts
RohanMukka Apr 21, 2025
b97d2cb
Merge branch 'groupD/rohan' of https://github.com/IPMS-Project/IPMS i…
RohanMukka Apr 21, 2025
67538dc
Delete server/jobs/reminderEmail.test.js
vikashbalajik Apr 21, 2025
f7ff1dc
Update SupervisorDashboard.js
vikashbalajik Apr 21, 2025
80d6f0b
resolved errors
RohanMukka Apr 21, 2025
cdeb333
fix
RohanMukka Apr 21, 2025
8cb77ab
fix
RohanMukka Apr 21, 2025
f020cbf
addressed comments
RohanMukka Apr 21, 2025
2b92578
Update index.js
vikashbalajik Apr 21, 2025
07194de
Update package.json
vikashbalajik Apr 21, 2025
1711ec8
Refactor SignUp component for improved readability and consistency
Charan-Nimmagadda Apr 21, 2025
4df7bce
Merge pull request #109 from IPMS-Project/GroupE-development
Charan-Nimmagadda Apr 21, 2025
4ad1e60
Merge branch 'main' of https://github.com/IPMS-Project/IPMS into grou…
RohanMukka Apr 22, 2025
ea42d71
reminderemeil
RohanMukka Apr 22, 2025
306f39c
supervisor.js
RohanMukka Apr 22, 2025
943c7ad
supervisor.js
RohanMukka Apr 22, 2025
8c967fb
resolved conflicts
RohanMukka Apr 22, 2025
472ad48
Update CoordinatorDashboard.js
vikashbalajik Apr 23, 2025
4a48dbc
Update SupervisorDashboard.css
vikashbalajik Apr 23, 2025
577463b
Update CoordinatorDashboard.js
vikashbalajik Apr 23, 2025
93e3528
Merge pull request #141 from IPMS-Project/groupD/rohan
Charan-Nimmagadda Apr 25, 2025
921e8d4
Merge branch 'GroupE-frontend' into GroupE/frontend
MRPHFitch Apr 25, 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,4 @@ dist
/client/package-lock.json
/server/package-lock.json

.DS_Store
122 changes: 122 additions & 0 deletions client/src/pages/CoordinatorCumulativeReviewForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import React, { useEffect, useState } from "react";
import { useParams, useNavigate } from "react-router-dom";
import axios from "axios";
import Swal from "sweetalert2";
import "../styles/CoordinatorCumulativeReviewForm.css";

const CoordinatorCumulativeReviewForm = () => {
const { groupIndex } = useParams();
const navigate = useNavigate();

const [reports, setReports] = useState([]);
const [coordinatorComment, setCoordinatorComment] = useState("");
const [supervisorComment, setSupervisorComment] = useState("");
const [loading, setLoading] = useState(true);

// Fetch the report group when component mounts
useEffect(() => {
const fetchGroup = async () => {
try {
const res = await axios.get(
`${process.env.REACT_APP_API_URL}/api/reports/cumulative/group/${groupIndex}`
);
if (res.data.success) {
const fetchedReports = res.data.group.reports || [];
setReports(fetchedReports);
setSupervisorComment(fetchedReports[0]?.supervisorComments || "Not available");
}
} catch (err) {
console.error("Failed to fetch group:", err);
} finally {
setLoading(false);
}
};

fetchGroup();
}, [groupIndex]);

// Submit coordinator comments for all reports in the group
const handleSubmit = async () => {
if (!coordinatorComment.trim()) {
Swal.fire("Error", "Comment cannot be empty.", "error");
return;
}

try {
const weeks = reports.map((report) => report.week); // extract weeks

await axios.post(`${process.env.REACT_APP_API_URL}/api/reports/coordinator-comments`, {
groupIndex: parseInt(groupIndex),
comments: coordinatorComment.trim(),
weeks,
});

Swal.fire("Success", "Coordinator comment submitted.", "success");

localStorage.setItem("reviewedGroupIndex", groupIndex); // ✅ For dashboard refresh
navigate("/coordinator-dashboard");
} catch (err) {
console.error("Failed to submit coordinator comment", err);
Swal.fire("Error", "Failed to submit comment. Please try again.", "error");
}
};


if (loading) {
return <p className="coordinator-review-container">Loading...</p>;
}

return (
<div className="coordinator-review-container">
<h2>Review Cumulative Weekly Reports</h2>

<table className="coordinator-review-table">
<thead>
<tr>
<th>Week</th>
<th>Hours</th>
<th>Tasks</th>
<th>Lessons</th>
</tr>
</thead>
<tbody>
{reports.map((report, idx) => (
<tr key={idx}>
<td>{report.week}</td>
<td>{report.hours}</td>
<td>{report.tasks}</td>
<td>{report.lessons}</td>
</tr>
))}
</tbody>
</table>

<div className="comment-section">
<label><strong>Supervisor Comment</strong></label>
<div className="readonly-display">{supervisorComment}</div>
</div>

<div className="comment-section">
<label htmlFor="coordinatorComments"><strong>Coordinator Comment</strong></label>
<textarea
id="coordinatorComments"
className="coordinator-comment-box"
placeholder="Add coordinator comments here..."
value={coordinatorComment}
onChange={(e) => setCoordinatorComment(e.target.value)}
/>
</div>

<div className="button-group">
<button className="cancel-btn" onClick={() => navigate("/coordinator-dashboard")}>
Cancel
</button>
<button className="submit-btn" onClick={handleSubmit}>
Submit Comment
</button>
</div>
</div>
);
};

export default CoordinatorCumulativeReviewForm;
136 changes: 106 additions & 30 deletions client/src/pages/CoordinatorDashboard.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,124 @@
import React, { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import axios from "axios";
import "../styles/dashboard.css";

function CoordinatorDashboard() {
const [requests, setRequests] = useState([]);
import { useNavigate } from "react-router-dom";
import "../styles/SupervisorDashboard.css";
const CoordinatorDashboard = () => {
const [activeTab, setActiveTab] = useState("requests"); // 'requests' or 'reports'
const navigate = useNavigate();
// TEAM A's Internship Requests Logic
const [requests, setRequests] = useState([]);
const [loadingRequests, setLoadingRequests] = useState(true);

const fetchRequests = async () => {
useEffect(() => {
if (activeTab === "requests") {
fetchInternshipRequests();
}
}, [activeTab]);

const fetchInternshipRequests = async () => {
try {
const res = await axios.get(
`${process.env.REACT_APP_API_URL}/api/coordinator/requests`
);
setRequests(res.data);
const res = await axios.get(`${process.env.REACT_APP_API_URL}/api/coordinator/requests`);
setRequests(res.data || []);
} catch (err) {
console.error("Failed to fetch requests:", err);
console.error("Error fetching internship requests:", err);
} finally {
setLoadingRequests(false);
}
};
// Group D's Weekly Report Review Logic

const [reportGroups, setReportGroups] = useState([]);
const [loadingReports, setLoadingReports] = useState(true);

useEffect(() => {
fetchRequests();
}, []);
if (activeTab === "reports") {
fetchReportGroups();
}
}, [activeTab]);

const fetchReportGroups = async () => {
try {
const res = await axios.get(`${process.env.REACT_APP_API_URL}/api/reports/supervised-groups`);
const filtered = res.data?.groups?.filter(group => {
const key = `coordinator_reviewed_${group.groupIndex}`;
return !localStorage.getItem(key);
});
setReportGroups(filtered || []);
} catch (err) {
console.error("Error fetching reports:", err);
} finally {
setLoadingReports(false);
}
};

const handleReviewClick = (group) => {
localStorage.setItem(`coordinator_reviewed_${group.groupIndex}`, "true");
navigate(`/review-cumulative/${group.groupIndex}`);
};


// Render UI

return (
<div className="dashboard-container">
<h2 className="dashboard-title">Coordinator Dashboard</h2>

{requests.length === 0 ? (
<p>No Pending Requests</p>
) : (
requests.map((req) => (
<div
key={req._id}
className="request-card"
onClick={() => navigate(`/coordinator/request/${req._id}`)}
>
{/* <h4>{req.student.userName}</h4>
<p>Email: {req.student.email}</p> */}
<p>Company: {req.workplace.name}</p>
</div>
))
<h2>Coordinator Dashboard</h2>

{/* Tabs */}
<div className="tab-toggle">
<button onClick={() => setActiveTab("requests")} className={activeTab === "requests" ? "active" : ""}>Internship Requests</button>
<button onClick={() => setActiveTab("reports")} className={activeTab === "reports" ? "active" : ""}>Weekly Reports Review</button>
</div>

{/* Tab: Internship Requests */}
{activeTab === "requests" && (
<>
{loadingRequests ? <p>Loading...</p> : (
<table className="dashboard-table">
<thead>
<tr>
<th>Student Name</th>
<th>Student ID</th>
<th>Company</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{requests.map(req => (
<tr key={req._id}>
<td>{req.studentName}</td>
<td>{req.studentId}</td>
<td>{req.companyName}</td>
<td>{req.status}</td>
</tr>
))}
</tbody>
</table>
)}
</>
)}

{/* Tab: Weekly Reports Review */}
{activeTab === "reports" && (
<>
{loadingReports ? <p>Loading reports...</p> : (
reportGroups.length === 0
? <p>No reports to review</p>
: reportGroups.map(group => (
<div className="report-group-card" key={group.groupIndex}>
<h4>Weeks: {group.weeks?.join(", ")}</h4>
<ul>
{group.reports.map((r, i) => (
<li key={i}>Week {r.week} — Hours: {r.hours} — Tasks: {r.tasks}</li>
))}
</ul>
<button onClick={() => handleReviewClick(group)}>Review & Comment</button>
</div>
))
)}
</>
)}
</div>
);
}
};

export default CoordinatorDashboard;
76 changes: 76 additions & 0 deletions client/src/pages/CoordinatorReviewForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import React, { useEffect, useState } from "react";
import axios from "axios";
import { useParams, useNavigate } from "react-router-dom";
import "../styles/CumulativeReviewForm.css";

const CoordinatorReviewForm = () => {
const { groupIndex } = useParams();
const navigate = useNavigate();
const [reports, setReports] = useState([]);
const [comment, setComment] = useState("");
const [message, setMessage] = useState("");

useEffect(() => {
const fetchGroup = async () => {
try {
const res = await axios.get(
`${process.env.REACT_APP_API_URL}/api/reports/group/${groupIndex}`
);
if (res.data.success) {
setReports(res.data.group.reports);
}
} catch (err) {
console.error("Failed to load group reports", err);
}
};

fetchGroup();
}, [groupIndex]);

const handleSubmit = async () => {
try {
const promises = reports.map((r) =>
axios.put(
`${process.env.REACT_APP_API_URL}/api/reports/${r._id}/coordinator-comment`,
{ coordinatorComments: comment }
)
);
await Promise.all(promises);
setMessage("Coordinator comment submitted successfully!");
setTimeout(() => navigate("/coordinator-dashboard"), 1500);
} catch (err) {
console.error("Failed to submit coordinator comments", err);
setMessage("Failed to submit comment.");
}
};

return (
<div className="cumulative-review-container">
<h2>Coordinator Review - Group {parseInt(groupIndex) + 1}</h2>

{reports.map((r, idx) => (
<div key={r._id} className="review-report-card">
<h4>Week {r.week}</h4>
<p><strong>Hours:</strong> {r.hours}</p>
<p><strong>Tasks:</strong> {r.tasks}</p>
<p><strong>Lessons:</strong> {r.lessons}</p>
<p><strong>Supervisor Comments:</strong> {r.supervisorComments}</p>
</div>
))}

<textarea
value={comment}
onChange={(e) => setComment(e.target.value)}
placeholder="Write your coordinator comments..."
></textarea>

<button onClick={handleSubmit} className="submit-button">
Submit Coordinator Comment
</button>

{message && <p className="form-message">{message}</p>}
</div>
);
};

export default CoordinatorReviewForm;
Loading