diff --git a/EduSync.exe b/EduSync.exe index 88f3197..0620a12 100644 Binary files a/EduSync.exe and b/EduSync.exe differ diff --git a/adminHandler.go b/adminHandler.go index 1570608..3137e80 100644 --- a/adminHandler.go +++ b/adminHandler.go @@ -166,6 +166,30 @@ func AdminHandler(router *mux.Router) { } }).Methods("POST") + // Handle student deletion + router.HandleFunc("/admin/student/delete/{googleID}", func(res http.ResponseWriter, req *http.Request) { + vars := mux.Vars(req) + googleID := vars["googleID"] + + if req.Method == http.MethodDelete { + student, err := readStudent(googleID, req) + if err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + + if err := deleteStudent(student, req); err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + + res.WriteHeader(http.StatusNoContent) // Respond with No Content status + return + } + + http.Error(res, "Method Not Allowed", http.StatusMethodNotAllowed) + }).Methods("DELETE") + // Serve the search parent page router.HandleFunc("/admin/search_parent", func(res http.ResponseWriter, req *http.Request) { t, err := template.ParseFiles("templates/admin/search_parent.html") @@ -266,6 +290,30 @@ func AdminHandler(router *mux.Router) { } }).Methods("POST") + // Handle parent deletion + router.HandleFunc("/admin/parent/delete/{googleID}", func(res http.ResponseWriter, req *http.Request) { + vars := mux.Vars(req) + googleID := vars["googleID"] + + if req.Method == http.MethodDelete { + parent, err := readParent(googleID, req) + if err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + + if err := deleteParent(parent, req); err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + + res.WriteHeader(http.StatusNoContent) // Respond with No Content status + return + } + + http.Error(res, "Method Not Allowed", http.StatusMethodNotAllowed) + }).Methods("DELETE") + // Serve the search instructor page router.HandleFunc("/admin/search_instructor", func(res http.ResponseWriter, req *http.Request) { t, err := template.ParseFiles("templates/admin/search_instructor.html") @@ -377,6 +425,30 @@ func AdminHandler(router *mux.Router) { t.Execute(res, nil) }).Methods("GET") + // Handle instructor deletion + router.HandleFunc("/admin/instructor/delete/{googleID}", func(res http.ResponseWriter, req *http.Request) { + vars := mux.Vars(req) + googleID := vars["googleID"] + + if req.Method == http.MethodDelete { + instructor, err := readInstructor(googleID, req) + if err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + + if err := deleteInstructor(instructor, req); err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + + res.WriteHeader(http.StatusNoContent) // Respond with No Content status + return + } + + http.Error(res, "Method Not Allowed", http.StatusMethodNotAllowed) + }).Methods("DELETE") + // Serve the create announcement page router.HandleFunc("/admin/create_announcement", func(res http.ResponseWriter, req *http.Request) { t, err := template.ParseFiles("templates/admin/create_announcement.html") @@ -473,6 +545,23 @@ func AdminHandler(router *mux.Router) { // Request Body: JSON object with announcement details // Response: HTTP Status Created (201) + // Handle announcement deletion + router.HandleFunc("/admin/announcement/delete/{announcementID}", func(res http.ResponseWriter, req *http.Request) { + vars := mux.Vars(req) + announcementID := vars["announcementID"] + + switch req.Method { + case http.MethodDelete: + if err := deleteAnnouncement(announcementID, req); err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + res.WriteHeader(http.StatusNoContent) + default: + http.Error(res, "Method Not Allowed", http.StatusMethodNotAllowed) + } + }).Methods("DELETE") + // Serve the search class page router.HandleFunc("/admin/search_class", func(res http.ResponseWriter, req *http.Request) { t, err := template.ParseFiles("templates/admin/search_class.html") @@ -589,6 +678,27 @@ func AdminHandler(router *mux.Router) { } }).Methods("POST") + // Handle class deletion + router.HandleFunc("/admin/class/delete/{classID}", func(res http.ResponseWriter, req *http.Request) { + vars := mux.Vars(req) + classID := vars["classID"] + + switch req.Method { + case http.MethodDelete: + // Create a Class struct with the classID + class := Class{ClassID: classID} + + // Call deleteClass function to delete the class + if err := deleteClass(class, req); err != nil { + http.Error(res, err.Error(), http.StatusInternalServerError) + return + } + res.WriteHeader(http.StatusNoContent) + default: + http.Error(res, "Method Not Allowed", http.StatusMethodNotAllowed) + } + }).Methods("DELETE") + router.HandleFunc("/admin/api/profile", func(res http.ResponseWriter, req *http.Request) { currentUser, err := GetCurrentUser(req) if err != nil { diff --git a/database.go b/database.go index 9ce2862..3c8fc1c 100644 --- a/database.go +++ b/database.go @@ -6,7 +6,6 @@ import ( "fmt" "log" "net/http" - "os" "strings" @@ -18,10 +17,10 @@ import ( var firebaseClient *db.Client func SessionCookie() (string, error) { - // sessionCookieStore := goDotEnvVariable("SESSION_COOKIE_STORE") - // if sessionCookieStore == "" { - // return sessionCookieStore, fmt.Errorf("SESSION_COOKIE_STORE is not set in the environment variables") - // } + // sessionCookieStore := goDotEnvVariable("SESSION_COOKIE_STORE") + // if sessionCookieStore == "" { + // return sessionCookieStore, fmt.Errorf("SESSION_COOKIE_STORE is not set in the environment variables") + // } sessionCookieStore, found := os.LookupEnv("COOKIESTORE") if !found { diff --git a/templates/admin/search_announcement.html b/templates/admin/search_announcement.html index 05630f7..a31da4d 100644 --- a/templates/admin/search_announcement.html +++ b/templates/admin/search_announcement.html @@ -9,12 +9,6 @@ - \ No newline at end of file + diff --git a/templates/admin/search_class.html b/templates/admin/search_class.html index ae4831d..39a511f 100644 --- a/templates/admin/search_class.html +++ b/templates/admin/search_class.html @@ -38,8 +38,13 @@ const duration = classObj.duration; const classDiv = document.createElement('div'); - classDiv.className = 'parent'; - classDiv.innerHTML = `${name.toUpperCase()} - ${dayMap[dayNum]} - ${time} - ${instructorName} - ${duration} hours`; + classDiv.className = 'class'; + classDiv.innerHTML = ` +
+ ${name.toUpperCase()} - ${dayMap[dayNum]} - ${time} - ${instructorName} - ${duration} hours + +
+ `; resultsDiv.appendChild(classDiv); }); } else { @@ -49,41 +54,7 @@ async function searchClasses() { const nameFilter = document.getElementById('nameFilter').value; - const response = await fetch(`/admin/api/search_class?name=${nameFilter}`); - - if (!response.ok) { - const errorMessage = `Error: ${response.status} - ${response.statusText}`; - throw new Error(errorMessage); - } - - const classes = await response.json(); - const resultsDiv = document.getElementById('results'); - resultsDiv.innerHTML = ''; - - const dayMap = { - '1': 'Monday', - '2': 'Tuesday', - '3': 'Wednesday', - '4': 'Thursday', - '5': 'Friday', - '6': 'Saturday', - '7': 'Sunday' - }; - - if (classes && classes.length > 0) { - classes.forEach(classObj => { - const [name, dayNum, time] = classObj.class_id.split('-'); - const instructorName = classObj.instructor; // Assuming this is the instructor name - const duration = classObj.duration; - - const classDiv = document.createElement('div'); - classDiv.className = 'parent'; - classDiv.innerHTML = `${name.toUpperCase()} - ${dayMap[dayNum]} - ${time} - ${instructorName} - ${duration} hours`; - resultsDiv.appendChild(classDiv); - }); - } else { - resultsDiv.innerHTML = 'No classes found.'; - } + await fetchClasses(`/admin/api/search_class?name=${nameFilter}`); } async function resetFilters() { @@ -91,6 +62,30 @@ await fetchClasses('/admin/api/search_class'); } + function confirmDelete(classID) { + if (confirm("Are you sure you want to delete this class?")) { + deleteClass(classID); + } + } + + async function deleteClass(classID) { + try { + const response = await fetch(`/admin/class/delete/${classID}`, { + method: 'DELETE', + }); + + if (response.ok) { + alert("Class deleted successfully."); + await searchClasses(); // Refresh the list + } else { + const errorMessage = `Error: ${response.status} - ${response.statusText}`; + throw new Error(errorMessage); + } + } catch (error) { + alert(`Failed to delete class: ${error.message}`); + } + } + window.onload = async function() { await fetchClasses('/admin/api/search_class'); } @@ -106,6 +101,43 @@ a:hover { text-decoration: underline; } + + /* Style the delete button */ + .delete-button { + background-color: #ff4d4d; /* Red background */ + color: white; /* White text */ + border: none; /* Remove default border */ + padding: 5px 10px; /* Add padding */ + margin-left: 10px; /* Space between button and other elements */ + cursor: pointer; /* Pointer cursor on hover */ + border-radius: 3px; /* Rounded corners */ + } + + .delete-button:hover { + background-color: #cc0000; /* Darker red on hover */ + } + + /* Style other action buttons with blue background */ + .action-button { + background-color: #246EB9; /* Blue background */ + color: white; /* White text */ + border: none; /* Remove default border */ + cursor: pointer; /* Pointer cursor on hover */ + border-radius: 3px; /* Rounded corners */ + } + + .action-button:hover { + background-color: #1b5791; /* Darker blue on hover */ + } + + /* Add space between class entries */ + .class-entry { + margin-bottom: 15px; /* Space between rows */ + } + + .class-entry a { + text-decoration: none; /* Remove underline from links */ + } diff --git a/templates/admin/search_instructor.html b/templates/admin/search_instructor.html index 8cd189e..eb208db 100644 --- a/templates/admin/search_instructor.html +++ b/templates/admin/search_instructor.html @@ -25,7 +25,12 @@ instructors.forEach(instructor => { const instructorDiv = document.createElement('div'); instructorDiv.className = 'parent'; - instructorDiv.innerHTML = `${instructor.name} - ${instructor.email}`; + instructorDiv.innerHTML = ` +
+ ${instructor.name} - ${instructor.email} + +
+ `; resultsDiv.appendChild(instructorDiv); }); } else { @@ -33,30 +38,35 @@ } } - async function searchInstructors() { - const nameFilter = document.getElementById('nameFilter').value; - const response = await fetch(`/admin/api/search_instructor?name=${nameFilter}`); - - if (!response.ok) { - const errorMessage = `Error: ${response.status} - ${response.statusText}`; - throw new Error(errorMessage); + function confirmDelete(instructorGoogleID) { + if (confirm("Are you sure you want to delete this instructor?")) { + deleteInstructor(instructorGoogleID); } - - const instructors = await response.json(); - const resultsDiv = document.getElementById('results'); - resultsDiv.innerHTML = ''; + } - if (instructors && instructors.length > 0) { - instructors.forEach(instructor => { - const instructorDiv = document.createElement('div'); - instructorDiv.className = 'parent'; - instructorDiv.innerHTML = `${instructor.name} - ${instructor.email}`; - resultsDiv.appendChild(instructorDiv); + async function deleteInstructor(instructorGoogleID) { + try { + const response = await fetch(`/admin/instructor/delete/${instructorGoogleID}`, { + method: 'DELETE', }); - } else { - resultsDiv.innerHTML = 'No instructors found.'; + + if (response.ok) { + alert("Instructor deleted successfully."); + await searchInstructors(); // Refresh the list + } else { + const errorMessage = `Error: ${response.status} - ${response.statusText}`; + throw new Error(errorMessage); + } + } catch (error) { + alert(`Failed to delete instructor: ${error.message}`); } } + + async function searchInstructors() { + const nameFilter = document.getElementById('nameFilter').value; + await fetchInstructors(`/admin/api/search_instructor?name=${nameFilter}`); + } + async function resetFilters() { document.getElementById('nameFilter').value = ''; @@ -79,6 +89,42 @@ a:hover { text-decoration: underline; /* Add underline on hover (optional) */ } + + .delete-button { + background-color: #ff4d4d; /* Red background for delete button */ + color: white; /* White text */ + border: none; /* Remove default border */ + padding: 5px 10px; /* Add padding */ + margin-left: 10px; /* Space between button and other elements */ + cursor: pointer; /* Pointer cursor on hover */ + border-radius: 3px; /* Rounded corners */ + } + + .delete-button:hover { + background-color: #cc0000; /* Darker red on hover */ + } + + /* Other buttons with #246EB9 background */ + .action-button { + background-color: #246EB9; /* Blue background */ + color: white; /* White text */ + border: none; /* Remove default border */ + cursor: pointer; /* Pointer cursor on hover */ + border-radius: 3px; /* Rounded corners */ + } + + .action-button:hover { + background-color: #1b5791; /* Darker blue on hover */ + } + + /* Add space between instructor entries */ + .instructor-entry { + margin-bottom: 15px; /* Space between rows */ + } + + .instructor-entry a { + text-decoration: none; /* Remove underline from links */ + } @@ -126,11 +172,11 @@

Search Instructors

    - +   - +   - +
diff --git a/templates/admin/search_parent.html b/templates/admin/search_parent.html index d565d88..7538c3b 100644 --- a/templates/admin/search_parent.html +++ b/templates/admin/search_parent.html @@ -25,7 +25,12 @@ parents.forEach(parent => { const parentDiv = document.createElement('div'); parentDiv.className = 'parent'; - parentDiv.innerHTML = `${parent.name} - ${parent.email}`; + parentDiv.innerHTML = ` +
+ ${parent.name} - ${parent.email} + +
+ `; resultsDiv.appendChild(parentDiv); }); } else { @@ -33,31 +38,35 @@ } } - async function searchParents() { - const nameFilter = document.getElementById('nameFilter').value; - const response = await fetch(`/admin/api/search_parent?name=${nameFilter}`); - - if (!response.ok) { - const errorMessage = `Error: ${response.status} - ${response.statusText}`; - throw new Error(errorMessage); + function confirmDelete(parentGoogleID) { + if (confirm("Are you sure you want to delete this parent?")) { + deleteParent(parentGoogleID); } - - const parents = await response.json(); - const resultsDiv = document.getElementById('results'); - resultsDiv.innerHTML = ''; + } - if (parents && parents.length > 0) { - parents.forEach(parent => { - const parentDiv = document.createElement('div'); - parentDiv.className = 'parent'; - parentDiv.innerHTML = `${parent.name} - ${parent.email}`; - resultsDiv.appendChild(parentDiv); + async function deleteParent(parentGoogleID) { + try { + const response = await fetch(`/admin/parent/delete/${parentGoogleID}`, { + method: 'DELETE', }); - } else { - resultsDiv.innerHTML = 'No parents found.'; + + if (response.ok) { + alert("Parent deleted successfully."); + await searchParents(); // Refresh the list + } else { + const errorMessage = `Error: ${response.status} - ${response.statusText}`; + throw new Error(errorMessage); + } + } catch (error) { + alert(`Failed to delete parent: ${error.message}`); } } + async function searchParents() { + const nameFilter = document.getElementById('nameFilter').value; + await fetchParents(`/admin/api/search_parent?name=${nameFilter}`); + } + async function resetFilters() { document.getElementById('nameFilter').value = ''; await fetchParents(`/admin/api/search_parent`); @@ -79,6 +88,42 @@ a:hover { text-decoration: underline; /* Add underline on hover (optional) */ } + + .delete-button { + background-color: #ff4d4d; /* Red background for delete button */ + color: white; /* White text */ + border: none; /* Remove default border */ + padding: 5px 10px; /* Add padding */ + margin-left: 10px; /* Space between button and other elements */ + cursor: pointer; /* Pointer cursor on hover */ + border-radius: 3px; /* Rounded corners */ + } + + .delete-button:hover { + background-color: #cc0000; /* Darker red on hover */ + } + + /* Other buttons with #246EB9 background */ + .action-button { + background-color: #246EB9; /* Blue background */ + color: white; /* White text */ + border: none; /* Remove default border */ + cursor: pointer; /* Pointer cursor on hover */ + border-radius: 3px; /* Rounded corners */ + } + + .action-button:hover { + background-color: #1b5791; /* Darker blue on hover */ + } + + /* Add space between parent entries */ + .parent-entry { + margin-bottom: 15px; /* Space between rows */ + } + + .parent-entry a { + text-decoration: none; /* Remove underline from links */ + } @@ -126,11 +171,11 @@

Search Parents

    - +   - +   - +
diff --git a/templates/admin/search_student.html b/templates/admin/search_student.html index 23bb887..0492e25 100644 --- a/templates/admin/search_student.html +++ b/templates/admin/search_student.html @@ -40,7 +40,15 @@ students.forEach(student => { const studentDiv = document.createElement('div'); studentDiv.className = 'student'; - studentDiv.innerHTML = `${student.name} - ${formatClassID(student.class_id)}`; + studentDiv.innerHTML = ` +
+ + ${student.name} + + - ${formatClassID(student.class_id)} + +
+ `; resultsDiv.appendChild(studentDiv); }); } else { @@ -48,6 +56,30 @@ } } + function confirmDelete(studentGoogleID) { + if (confirm("Are you sure you want to delete this student?")) { + deleteStudent(studentGoogleID); + } + } + + async function deleteStudent(studentGoogleID) { + try { + const response = await fetch(`/admin/student/delete/${studentGoogleID}`, { + method: 'DELETE', + }); + + if (response.ok) { + alert("Student deleted successfully."); + await searchStudents(); // Refresh the list + } else { + const errorMessage = `Error: ${response.status} - ${response.statusText}`; + throw new Error(errorMessage); + } + } catch (error) { + alert(`Failed to delete student: ${error.message}`); + } + } + async function searchStudents() { const nameFilter = document.getElementById('nameFilter').value; const classFilter = document.getElementById('classFilter').value; @@ -76,6 +108,51 @@ a:hover { text-decoration: underline; /* Add underline on hover (optional) */ } + + button { + background-color: #ff4d4d; /* Red background */ + color: white; /* White text */ + border: none; /* Remove default border */ + margin-left: 10px; /* Space between button and other elements */ + cursor: pointer; /* Pointer cursor on hover */ + border-radius: 3px; /* Rounded corners */ + } + + .delete-button { + background-color: #ff4d4d; /* Red background for delete button */ + color: white; /* White text */ + border: none; /* Remove default border */ + padding: 5px 10px; /* Add padding */ + margin-left: 10px; /* Space between button and other elements */ + cursor: pointer; /* Pointer cursor on hover */ + border-radius: 3px; /* Rounded corners */ + } + + .delete-button:hover { + background-color: #cc0000; /* Darker red on hover */ + } + + /* Other buttons with #246EB9 background */ + .action-button { + background-color: #246EB9; /* Blue background */ + color: white; /* White text */ + border: none; /* Remove default border */ + cursor: pointer; /* Pointer cursor on hover */ + border-radius: 3px; /* Rounded corners */ + } + + .action-button:hover { + background-color: #1b5791; /* Darker blue on hover */ + } + + /* Add space between student entries */ + .student-entry { + margin-bottom: 15px; /* Space between rows */ + } + + .student-entry a { + text-decoration: none; /* Remove underline from links */ + } @@ -125,11 +202,11 @@

Update Student Details

   - +   - +   - +