-
Notifications
You must be signed in to change notification settings - Fork 7
Group d/sprint 3 #141
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Group d/sprint 3 #141
Changes from all commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
712c2ae
Refactor: Integrated supervisor reminder mail using cron jobs
vikashbalajik aea0a18
Revert "Group d/vikash sprint2"
vikashbalajik 5486ded
Revert "Group d/vikash sprint2"
vikashbalajik 52ceb76
Update cronUtils.test.js
vikashbalajik 899380f
Update cronUtils.test.js
vikashbalajik 3abc6b2
Fix: Integrated cron jobs & API routes properly for 4-week report
vikashbalajik acd2eb5
sprint 2 another way
vikashbalajik 370c3a4
✅ Final: show supervisor comments in read-only weekly report
vikashbalajik 388b88f
sprint 3
vikashbalajik 95d2007
Merge branch 'main' of https://github.com/IPMS-Project/IPMS into grou…
RohanMukka 21f01ce
Update reminderEmail.js
vikashbalajik 1857e69
resolved conflicts
RohanMukka b97d2cb
Merge branch 'groupD/rohan' of https://github.com/IPMS-Project/IPMS i…
RohanMukka 67538dc
Delete server/jobs/reminderEmail.test.js
vikashbalajik f7ff1dc
Update SupervisorDashboard.js
vikashbalajik 80d6f0b
resolved errors
RohanMukka cdeb333
fix
RohanMukka 8cb77ab
fix
RohanMukka f020cbf
addressed comments
RohanMukka 2b92578
Update index.js
vikashbalajik 07194de
Update package.json
vikashbalajik 4ad1e60
Merge branch 'main' of https://github.com/IPMS-Project/IPMS into grou…
RohanMukka ea42d71
reminderemeil
RohanMukka 306f39c
supervisor.js
RohanMukka 943c7ad
supervisor.js
RohanMukka 8c967fb
resolved conflicts
RohanMukka 472ad48
Update CoordinatorDashboard.js
vikashbalajik 4a48dbc
Update SupervisorDashboard.css
vikashbalajik 577463b
Update CoordinatorDashboard.js
vikashbalajik File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -141,3 +141,4 @@ dist | |
| /client/package-lock.json | ||
| /server/package-lock.json | ||
|
|
||
| .DS_Store | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.