diff --git a/client/package.json b/client/package.json index f8e56288..0eae406b 100644 --- a/client/package.json +++ b/client/package.json @@ -10,8 +10,11 @@ "axios": "^1.8.2", "bootstrap": "^5.3.5", "client": "file:", + "date-fns": "^4.1.0", + "dayjs": "^1.11.13", "react": "^19.0.0", "react-bootstrap": "^2.10.9", + "react-datepicker": "^8.3.0", "react-dom": "^19.0.0", "react-icons": "^5.5.0", "react-router-dom": "^7.4.1", @@ -19,9 +22,7 @@ "react-signature-canvas": "^1.1.0-alpha.2", "react-toastify": "^11.0.5", "sweetalert2": "^11.17.2", - "web-vitals": "^2.1.4", - "date-fns": "^4.1.0", - "react-datepicker": "^8.3.0" + "web-vitals": "^2.1.4" }, "scripts": { "start": "react-scripts start", diff --git a/client/src/pages/A1InternshipRequestForm.js b/client/src/pages/A1InternshipRequestForm.js index 82950c9c..cda45327 100644 --- a/client/src/pages/A1InternshipRequestForm.js +++ b/client/src/pages/A1InternshipRequestForm.js @@ -277,8 +277,8 @@ const A1InternshipRequestForm = ({ userRole = "student" }) => { } }; - const sendTaskDescriptions = async (descriptions) => { - try { + const sendTaskDescriptions = async (descriptions) => { + try { const response = await fetch(`${process.env.REACT_APP_API_URL}/api/align-outcomes`, { method: "POST", headers: { "Content-Type": "application/json" }, @@ -655,4 +655,4 @@ const A1InternshipRequestForm = ({ userRole = "student" }) => { ); }; -export default A1InternshipRequestForm; \ No newline at end of file +export default A1InternshipRequestForm; diff --git a/client/src/pages/CoordinatorDashboard.js b/client/src/pages/CoordinatorDashboard.js index ed712155..f3c20fec 100644 --- a/client/src/pages/CoordinatorDashboard.js +++ b/client/src/pages/CoordinatorDashboard.js @@ -1,17 +1,21 @@ import React, { useEffect, useState } from "react"; import axios from "axios"; import { useNavigate } from "react-router-dom"; -import "../styles/SupervisorDashboard.css"; +import "../styles/SupervisorDashboard.css"; // Reuse styling + const CoordinatorDashboard = () => { - const [activeTab, setActiveTab] = useState("requests"); // 'requests' or 'reports' + const [activeTab, setActiveTab] = useState("requests"); const navigate = useNavigate(); - // TEAM A's Internship Requests Logic const [requests, setRequests] = useState([]); const [loadingRequests, setLoadingRequests] = useState(true); + const [reportGroups, setReportGroups] = useState([]); + const [loadingReports, setLoadingReports] = useState(true); useEffect(() => { if (activeTab === "requests") { fetchInternshipRequests(); + } else if (activeTab === "reports") { + fetchReportGroups(); } }, [activeTab]); @@ -25,57 +29,96 @@ const CoordinatorDashboard = () => { setLoadingRequests(false); } }; - // Group D's Weekly Report Review Logic - - const [reportGroups, setReportGroups] = useState([]); - const [loadingReports, setLoadingReports] = useState(true); - - useEffect(() => { - 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); + const response = await axios.get(`${process.env.REACT_APP_API_URL}/api/reports/supervised-groups`); + console.log("Fetched report groups:", response.data); + setReportGroups(response.data.groups || []); + } catch (error) { + console.error("Error fetching reports:", error); } finally { setLoadingReports(false); } }; const handleReviewClick = (group) => { - localStorage.setItem(`coordinator_reviewed_${group.groupIndex}`, "true"); - navigate(`/review-cumulative/${group.groupIndex}`); + localStorage.setItem("reviewEmail", group.email); + localStorage.setItem("reviewWeeks", JSON.stringify(group.weeks)); + navigate(`/coordinator-review/${group.groupIndex}`); + }; + + const getSupervisorComment = (group) => { + if (!group.reports || group.reports.length === 0) { + return "No comment provided."; + } + const comments = group.reports + .map(r => r.supervisorComments?.trim()) + .filter(c => c && c !== ""); + const uniqueComments = [...new Set(comments)]; + + if (uniqueComments.length === 1) { + return uniqueComments[0]; + } else if (uniqueComments.length > 1) { + return uniqueComments.join(", "); + } else { + return "No comment provided."; + } + }; + + const formatWeeks = (weeks) => { + return weeks.map(w => w.split(" ")[1]).join(", "); }; - - // Render UI - return ( -
-

Coordinator Dashboard

+
+

+ Coordinator Dashboard +

{/* Tabs */} -
- - +
+ +
- {/* Tab: Internship Requests */} + {/* Internship Requests */} {activeTab === "requests" && ( <> - {loadingRequests ?

Loading...

: ( - + {loadingRequests ? ( +

Loading internship requests...

+ ) : ( +
- + @@ -83,8 +126,8 @@ const CoordinatorDashboard = () => { - {requests.map(req => ( - + {requests.map((req) => ( + @@ -97,23 +140,86 @@ const CoordinatorDashboard = () => { )} - {/* Tab: Weekly Reports Review */} + {/* Weekly Reports Review */} {activeTab === "reports" && ( <> - {loadingReports ?

Loading reports...

: ( - reportGroups.length === 0 - ?

No reports to review

- : reportGroups.map(group => ( -
-

Weeks: {group.weeks?.join(", ")}

-
    - {group.reports.map((r, i) => ( -
  • Week {r.week} — Hours: {r.hours} — Tasks: {r.tasks}
  • - ))} -
- + {loadingReports ? ( +

Loading weekly reports...

+ ) : ( + <> + {reportGroups.length === 0 ? ( +
+

No pending groups for review.

- )) + ) : ( + reportGroups.map((group) => ( +
e.currentTarget.style.transform = "scale(1.02)"} + onMouseOut={(e) => e.currentTarget.style.transform = "scale(1)"} + > +

+ Weeks: {formatWeeks(group.weeks)}{group.reports?.[0]?.name ? ` of ${group.reports[0].name}` : ""} +

+ +
+ + {group.reports?.[0]?.supervisorName ? `(${group.reports[0].supervisorName})` : ""} Supervisor's Comment: + +
+ {getSupervisorComment(group)} +
+
+ +
+ +
+
+ )) + )} + )} )} diff --git a/client/src/pages/CoordinatorReviewForm.js b/client/src/pages/CoordinatorReviewForm.js index e3f793c3..3b415d7f 100644 --- a/client/src/pages/CoordinatorReviewForm.js +++ b/client/src/pages/CoordinatorReviewForm.js @@ -1,74 +1,111 @@ import React, { useEffect, useState } from "react"; +import { useNavigate } from "react-router-dom"; import axios from "axios"; -import { useParams, useNavigate } from "react-router-dom"; -import "../styles/CumulativeReviewForm.css"; +import "../styles/CoordinatorCumulativeReviewForm.css"; const CoordinatorReviewForm = () => { - const { groupIndex } = useParams(); const navigate = useNavigate(); - const [reports, setReports] = useState([]); - const [comment, setComment] = useState(""); - const [message, setMessage] = useState(""); + const [group, setGroup] = useState(null); + const [coordinatorComment, setCoordinatorComment] = useState(""); + const [loading, setLoading] = useState(true); + + // Get review details saved in localStorage + const email = localStorage.getItem("reviewEmail"); + const weeks = JSON.parse(localStorage.getItem("reviewWeeks")); useEffect(() => { const fetchGroup = async () => { try { - const res = await axios.get( - `${process.env.REACT_APP_API_URL}/api/reports/group/${groupIndex}` + const response = await axios.post( + `${process.env.REACT_APP_API_URL}/api/reports/fetch-group`, + { email, weeks } ); - if (res.data.success) { - setReports(res.data.group.reports); - } - } catch (err) { - console.error("Failed to load group reports", err); + console.log("Fetched Group:", response.data); + setGroup(response.data.group); + } catch (error) { + console.error("Failed to load group reports", error); + } finally { + setLoading(false); } }; - - fetchGroup(); - }, [groupIndex]); + + if (email && weeks) { + fetchGroup(); + } else { + console.error("Review email or weeks not found in localStorage"); + setLoading(false); + } + }, [email, weeks]); 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."); + await axios.post(`${process.env.REACT_APP_API_URL}/api/reports/coordinator-comments`, { + email, + comments: coordinatorComment, + weeks, + }); + alert("Coordinator comment submitted successfully!"); + navigate("/coordinator-dashboard"); + } catch (error) { + console.error("Error submitting coordinator comments", error.response?.data || error.message); + alert("Something went wrong. Please try again."); } }; + if (loading) return
Loading cumulative review...
; + if (!group) return
No group data found.
; + + const studentName = group.reports[0]?.name || "Student"; + const supervisorComment = group.reports[0]?.supervisorComments || "No comment provided."; + const supervisorName = group.reports[0]?.supervisorName || "Supervisor"; + return (
-

Coordinator Review - Group {parseInt(groupIndex) + 1}

+

+ Cumulative Weekly Report of {studentName} for Weeks {group.weeks.map(w => w.split(" ")[1]).join(", ")} +

- {reports.map((r, idx) => ( -
-

Week {r.week}

-

Hours: {r.hours}

-

Tasks: {r.tasks}

-

Lessons: {r.lessons}

-

Supervisor Comments: {r.supervisorComments}

-
- ))} +
Student Name Student ID Company
{req.studentName} {req.studentId} {req.companyName}
+ + + + + + + + + + {group.reports.map((report, idx) => ( + + + + + + + ))} + +
WeekHoursTasksLessons
{report.week}{report.hours}{report.tasks}{report.lessons}
- +
+ Supervisor ({supervisorName}) Comment: +

{supervisorComment}

+
- +