diff --git a/app/CalculatorSessions/page.js b/app/CalculatorSessions/page.js index dee60b1..60cc14e 100644 --- a/app/CalculatorSessions/page.js +++ b/app/CalculatorSessions/page.js @@ -1,6 +1,7 @@ "use client"; import React, { useState, useEffect } from "react"; +import axios from "axios"; import { Card, CardHeader, @@ -10,102 +11,133 @@ import { } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; +import { useRouter } from "next/navigation"; function SavedSessions() { + const router = useRouter(); + // State to store all saved sessions and user search text const [sessions, setSessions] = useState([]); + const [grades, setGrades] = useState([]); const [search, setSearch] = useState(""); - // Utilize local storage to load saved sessions + const fetchGrades = async () => { + try { + const userId = 1; + const response = await axios.get( + `http://localhost:8080/api/grade-calculator/grade-entries/${userId}` + ); + console.log(response.data); + setGrades(response.data); + } catch (error) { + console.error("Error fetching saved sessions: ", error); + // setSessions([]); + } + }; useEffect(() => { - const saved = JSON.parse(localStorage.getItem("savedSessions")) || []; - setSessions(saved); + fetchGrades(); }, []); - // Function to delete a session via ID - const handleDelete = (id) => { - // remove session from the session list - const updated = sessions.filter((session) => session.id !== id); - setSessions(updated); + // axios function to delete a session via ID + const handleDelete = async (id) => { + try { + await axios.delete( + `http://localhost:8080/api/grade-calculator/grade-entry/${id}` + ); + // remove session from the session list + const updated = grades.filter((grade) => grade.id !== id); + setGrades(updated); + } catch (error) { + console.error("Error deleting session: ", error); + alert("Failed to delete session. Please try again."); + } + }; + + // handle edit by redirecting user to Grade Calculator via entry ID + const handleEdit = (id) => { + router.push(`/GradeCalculator?editId=$[id]`); }; - // Filter sessions based on user search text input (note: case-insensitive) - const filteredSessions = sessions.filter((session) => - session.title.toLowerCase().includes(search.toLowerCase()) + // Filter assignments based on user search text input (note: case-insensitive) + const filteredGrades = grades.filter((grade) => + grade.assignment_name.toLowerCase().includes(search.toLowerCase()) ); return (
- {/* Page Title*/} -

Saved Sessions

+ {/* Page Title and "Save Session" button aligned right*/} +
+

+ Saved Assignments +

+ +
{/* Search Input */} setSearch(e.target.value)} className="mb-4 w-full max-w-lg" /> - {/* List of filtered sessions */} + {/* List of assignments */}
- {filteredSessions.map((session) => ( + {filteredGrades.map((grade) => ( -
- - {session.title} - - - {/* Assignments within session card */} - - {session.assignments.map((a, i) => ( -
- {a.assignment_type} - {a.assignment_name} - {a.grade} % - {a.weight}% -
- ))} -
-
- - {/* Delete button and trash icon*/} - - */} + + Delete +
))} - {/* Message to user when no sessions match their text search*/} - {filteredSessions.length === 0 && ( -

No sessions found.

+ {grades.length === 0 && ( +

No grades found.

)}
); } -export default SavedSessions; \ No newline at end of file +export default SavedSessions; diff --git a/app/GradeCalculator/page.js b/app/GradeCalculator/page.js index 2157c34..69af2b2 100644 --- a/app/GradeCalculator/page.js +++ b/app/GradeCalculator/page.js @@ -1,30 +1,39 @@ "use client"; -import React, { useState, useEffect } from "react"; +import axios from "axios"; import { useRouter } from "next/navigation"; +import { useEffect, useState } from "react"; import { Card, - CardHeader, - CardTitle, - CardDescription, CardContent, + CardDescription, CardFooter, + CardHeader, + CardTitle, } from "@/components/ui/card"; -import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { CalculatorSelect, - CalculatorSelectTrigger, CalculatorSelectContent, CalculatorSelectItem, + CalculatorSelectTrigger, CalculatorSelectValue, } from "@/components/ui/calculator-select"; +import { Input } from "@/components/ui/input"; function GradeCalculator() { const router = useRouter(); - // State for session title and the list of assignments + const [editTitle, setEditTitle] = useState(null); + + useEffect(() => { + const params = new URLSearchParams(window.location.search); + const title = params.get("editTitle"); + if (title) setEditTitle(title); + }, []); + + // States for session title and the list of assignments const [title, setTitle] = useState(""); const [assignments, setAssignments] = useState([ { assignment_type: "", assignment_name: "", grade: "", weight: "" }, @@ -33,13 +42,48 @@ function GradeCalculator() { // state to hold calculated final grade const [finalGrade, setFinalGrade] = useState(null); + // fetch assignments for editing if editTitle is present + useEffect(() => { + if (editTitle) { + const fetchSessionAssignments = async () => { + try { + const userId = 1; // hardcoded for now + const response = await axios.get( + `http://localhost:8080/api/grade-calculator/grade-entries/${userId}` + ); + // filter assignments matching this session title + const sessionAssignments = response.data.filter( + (a) => a.session_title === editTitle + ); + if (sessionAssignments.length) { + setTitle(editTitle); + // map assignments and include `id` for PUT updates + setAssignments( + sessionAssignments.map((a) => ({ + id: a.id, + assignment_type: a.assignment_type, + assignment_name: a.assignment_name, + grade: a.assignment_grade, + weight: a.assignment_weight, + })) + ); + } + } catch (error) { + console.error("Error fetching session for edit:", error); + } + }; + fetchSessionAssignments(); + } + }, [editTitle]); + // handle general changes to assignment fields const handleChange = (index, field, value) => { - // Ensure values between 0-100 for grade & weight + // Restrict values between 0-100 for grade & weight if (field === "grade" || field === "weight") { const num = Number(value); if (num < 0 || num > 100) return; } + // Make a copy of the assignments, change the specific field in row, then update state const updated = [...assignments]; updated[index][field] = value; @@ -95,141 +139,177 @@ function GradeCalculator() { } }; - const handleSaveSession = () => { + // Save current session (total & calculated grade) + const handleSaveSession = async () => { // Check if title is empty or if there are no assignments defined - if (!title || assignments.length === 0) { - alert("Please provide a sesssion title and at least one assignment"); + if (!title || !finalGrade) { + alert( + "Please calculate grade and provide a sesssion title before saving" + ); return; } - // Get previously saved sessions from local storage. Use empty array if no saved sessions exist (note: reason for local storage is to develop MVP for saved sessions page) - const savedSessions = - JSON.parse(localStorage.getItem("savedSessions")) || []; - // add session to the list - savedSessions.push({ id: Date.now(), title, assignments }); - localStorage.setItem("savedSessions", JSON.stringify(savedSessions)); + try { + for (let assignment of assignments) { + if (assignment.id) { + await axios.put(`http://localhost:8080/api/Calculator/grade-entry`, { + assignment_grade: assignment.grade, + assignment_weight: assignment.weight, + }); + } else { + // POST axios call to new-grade-entry. "If not editing, then create new session + await axios.post( + `http://localhost:8080/api/grade-calculator/new-grade-entry`, + { + user_id: 1, + assignment_type: assignment.assignment_type, + assignment_name: assignment.assignment_name, + assignment_grade: assignment.grade, + assignment_weight: assignment.weight, + } + ); + } + } - // redirect to saved sessions page - router.push("/CalculatorSessions"); + // After saving to db, redirect user to saved assignments page + router.push("/CalculatorSessions"); + } catch (error) { + console.error("Error saving grade entry: ", error); + alert("Failed to save assignment(s). Please try again."); + } }; return ( -
- - - Grade Calculator - - Enter grades and weights to calculate your grade. - - - - - setTitle(e.target.value)} - /> - - {/*Column headers */} -
- Assignment Type - Assignment Name - Grade (%) - Weight (%) - {/* trash icon column */} - -
- - {/* Assignment rows */} - {assignments.map((assignments, index) => ( -
- {/* Assignment type dropdown */} - - handleChange(index, "assignment_type", value) - } - > - - - - - Homework - Quiz - Midterm - Final Exam - - - - {/* Assignment Name Input*/} - - handleChange(index, "assignment_name", e.target.value) - } - /> - - {/* Grade Input */} - handleChange(index, "grade", e.target.value)} - /> - - {/* Weight Input */} - handleChange(index, "weight", e.target.value)} - /> - - {/* Delete assignment button */} - +
+ + + Grade Calculator + + Enter grades and weights to calculate your grade. + + + + + setTitle(e.target.value)} + /> + + {/*Column headers */} +
+ Assignment Type + Assignment Name + Grade (%) + Weight (%) + {/* trash icon column */} +
- ))} - {/* Add another assignment button */} - -
+ {/* Assignment rows */} + {assignments.map((assignments, index) => ( +
+ {/* Assignment type dropdown */} + + handleChange(index, "assignment_type", value) + } + > + + + + + + Homework + + + Quiz + + + Midterm + + + Final Exam + + + + + {/* Assignment Name Input*/} + + handleChange(index, "assignment_name", e.target.value) + } + /> + + {/* Grade Input */} + handleChange(index, "grade", e.target.value)} + /> + + {/* Weight Input */} + + handleChange(index, "weight", e.target.value) + } + /> + + {/* Delete assignment button */} + +
+ ))} + - - - + {/* Footer with action buttons */} + + + + + + - {/* Display calculated final grade */} + {/* Display calculated grade */} {finalGrade && ( -

Final Grade: {finalGrade}

+

+ Final Grade: {finalGrade} +

)} -
-
-
+ +
); } diff --git a/components/NavBar.jsx b/components/NavBar.jsx index 65ef63e..eac1cb8 100644 --- a/components/NavBar.jsx +++ b/components/NavBar.jsx @@ -18,6 +18,7 @@ const navigationLinks = [ { href: "/LockInChat", label: "Study with AI", active: false }, { href: "/StudySession", label: "Study Timer", active: false }, { href: "/Tasks", label: "Tasks", active: false }, + { href: "/GradeCalculator", label: "Grade Calculator", active: false }, ]; export default function NavBarComponent() {