Skip to content
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

Announcement #37

Merged
merged 20 commits into from
Aug 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Binary file modified EduSync.exe
Binary file not shown.
159 changes: 135 additions & 24 deletions adminHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,25 @@ package main

import (
"encoding/json"
"fmt"
"html/template"
"net/http"
"time"

"github.com/google/uuid"
"github.com/gorilla/mux"
)

// AdminHandler handles all admin-related routes.
//
// This handler is responsible for setting up routes for admin-related tasks,
// such as searching for students, parents, instructors, and announcements,
// as well as editing and updating their information.
//
// Example usage:
// router := mux.NewRouter()
// AdminHandler(router)

func AdminHandler(router *mux.Router) {
router.HandleFunc("/admin", func(res http.ResponseWriter, req *http.Request) {
t, err := template.ParseFiles("templates/admin/index.html")
Expand All @@ -27,6 +40,7 @@ func AdminHandler(router *mux.Router) {
t.Execute(res, nil)
}).Methods("GET")

// Search for students by name and class
router.HandleFunc("/admin/api/search_student", func(res http.ResponseWriter, req *http.Request) {
name := req.URL.Query().Get("name")
class := req.URL.Query().Get("class")
Expand All @@ -39,6 +53,11 @@ func AdminHandler(router *mux.Router) {
json.NewEncoder(res).Encode(students)
}).Methods("GET")

// Example usage:
// GET /admin/api/search_student?name=John&class=10th
// Response: JSON list of students matching the search criteria

// Edit student information
router.HandleFunc("/admin/student/{googleID}/edit", func(res http.ResponseWriter, req *http.Request) {
vars := mux.Vars(req)
googleID := vars["googleID"]
Expand All @@ -57,6 +76,11 @@ func AdminHandler(router *mux.Router) {
t.Execute(res, student)
}).Methods("GET")

// Example usage:
// GET /admin/student/1234567890/edit
// Response: HTML form to edit student information

// Update student information
router.HandleFunc("/admin/student/{googleID}", func(res http.ResponseWriter, req *http.Request) {
vars := mux.Vars(req)
googleID := vars["googleID"]
Expand Down Expand Up @@ -84,6 +108,11 @@ func AdminHandler(router *mux.Router) {
}
}).Methods("GET", "PUT")

// Example usage:
// PUT /admin/student/1234567890
// Request Body: JSON object with updated student information
// Response: HTTP Status No Content (204)

router.HandleFunc("/admin/search_parent", func(res http.ResponseWriter, req *http.Request) {
t, err := template.ParseFiles("templates/admin/search_parent.html")
if err != nil {
Expand All @@ -108,12 +137,6 @@ func AdminHandler(router *mux.Router) {
vars := mux.Vars(req)
googleID := vars["googleID"]

// currentUser, err := GetCurrentUser(req)
// if err != nil {
// http.Error(res, "Unauthorized", http.StatusUnauthorized)
// return
// }

parent, err := readParent(googleID, req)
if err != nil {
http.Error(res, err.Error(), http.StatusInternalServerError)
Expand All @@ -132,12 +155,6 @@ func AdminHandler(router *mux.Router) {
vars := mux.Vars(req)
googleID := vars["googleID"]

// currentUser, err := GetCurrentUser(req)
// if err != nil {
// http.Error(res, "Unauthorized", http.StatusUnauthorized)
// return
// }

switch req.Method {
case http.MethodGet:
parent, err := readParent(googleID, req)
Expand Down Expand Up @@ -185,12 +202,6 @@ func AdminHandler(router *mux.Router) {
vars := mux.Vars(req)
googleID := vars["googleID"]

// currentUser, err := GetCurrentUser(req)
// if err != nil {
// http.Error(res, "Unauthorized", http.StatusUnauthorized)
// return
// }

instructor, err := readInstructor(googleID, req)
if err != nil {
http.Error(res, err.Error(), http.StatusInternalServerError)
Expand All @@ -209,12 +220,6 @@ func AdminHandler(router *mux.Router) {
vars := mux.Vars(req)
googleID := vars["googleID"]

// currentUser, err := GetCurrentUser(req)
// if err != nil {
// http.Error(res, "Unauthorized", http.StatusUnauthorized)
// return
// }

switch req.Method {
case http.MethodGet:
instructor, err := readInstructor(googleID, req)
Expand All @@ -237,4 +242,110 @@ func AdminHandler(router *mux.Router) {
res.WriteHeader(http.StatusNoContent)
}
}).Methods("GET", "PUT")

//Serve the search announcement page
router.HandleFunc("/admin/search_announcement", func(res http.ResponseWriter, req *http.Request) {
t, err := template.ParseFiles("templates/admin/search_announcement.html")
if err != nil {
http.Error(res, err.Error(), http.StatusInternalServerError)
return
}
t.Execute(res, nil)
}).Methods("GET")

// 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")
if err != nil {
http.Error(res, err.Error(), http.StatusInternalServerError)
return
}
t.Execute(res, nil)
}).Methods("GET")

// Search for an announcement
router.HandleFunc("/admin/api/search_announcement", func(res http.ResponseWriter, req *http.Request) {
subject := req.URL.Query().Get("subject")
// content := req.URL.Query().Get("content")
announcements, err := searchAnnouncements(subject)
if err != nil {
http.Error(res, err.Error(), http.StatusInternalServerError)
return
}
res.Header().Set("Content-Type", "application/json")
json.NewEncoder(res).Encode(announcements)
}).Methods("GET")

router.HandleFunc("/admin/announcement/{announcementID}/edit", func(res http.ResponseWriter, req *http.Request) {
vars := mux.Vars(req)
announcementID := vars["announcementID"]

announcement, err := readAnnouncement(announcementID, req)
if err != nil {
http.Error(res, err.Error(), http.StatusInternalServerError)
return
}

t, err := template.ParseFiles("templates/admin/edit_announcement.html")
if err != nil {
http.Error(res, err.Error(), http.StatusInternalServerError)
return
}
t.Execute(res, announcement)
}).Methods("GET")

router.HandleFunc("/admin/announcement/{announcementID}", func(res http.ResponseWriter, req *http.Request) {
vars := mux.Vars(req)
announcementID := vars["announcementID"]

switch req.Method {
case http.MethodGet:
announcement, err := readAnnouncement(announcementID, req)
if err != nil {
http.Error(res, err.Error(), http.StatusInternalServerError)
return
}
res.Header().Set("Content-Type", "application/json")
json.NewEncoder(res).Encode(announcement)
case http.MethodPut:
var updates map[string]interface{}
if err := json.NewDecoder(req.Body).Decode(&updates); err != nil {
http.Error(res, err.Error(), http.StatusBadRequest)
return
}
if err := updateAnnouncement(announcementID, updates, req); err != nil {
http.Error(res, err.Error(), http.StatusInternalServerError)
return
}
res.WriteHeader(http.StatusNoContent)
}
}).Methods("GET", "PUT")

// Create a new announcement
router.HandleFunc("/admin/announcement/", func(res http.ResponseWriter, req *http.Request) {
switch req.Method {
case http.MethodPost:
var announcement Announcement
if err := json.NewDecoder(req.Body).Decode(&announcement); err != nil {
http.Error(res, fmt.Sprintf(`{"error": "Invalid request payload: %v"}`, err), http.StatusBadRequest)
return
}
announcement.AnnouncementID = uuid.New().String()
announcement.CreatedAt = time.Now()
announcement.UpdatedAt = time.Now()
if err := createAnnouncement(announcement, req); err != nil {
http.Error(res, fmt.Sprintf(`{"error": "Failed to create announcement: %v"}`, err), http.StatusInternalServerError)
return
}
res.WriteHeader(http.StatusCreated)
json.NewEncoder(res).Encode(announcement)
default:
http.Error(res, `{"error": "Invalid request method"}`, http.StatusMethodNotAllowed)
}
}).Methods("POST")

// Example usage:
// POST /admin/announcement
// Request Body: JSON object with announcement details
// Response: HTTP Status Created (201)
}
2 changes: 1 addition & 1 deletion config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"fmt"
"os"
)

// Config struct to hold the configuration
type Config struct {
GoogleClientID string `json:"google_client_id"`
Expand Down
46 changes: 41 additions & 5 deletions database.go
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,42 @@ func readAnnouncement(announcementID string, req *http.Request) (Announcement, e
return announcement, nil
}

func updateAnnouncement(announcement Announcement, updates map[string]interface{}, req *http.Request) error {
func readAnnouncements() ([]Announcement, error) {
var announcementsMap map[string]Announcement
ref := firebaseClient.NewRef("announcements")
if err := ref.Get(context.TODO(), &announcementsMap); err != nil {
return nil, fmt.Errorf("error reading announcements: %v", err)
}
// Convert map to slice
announcements := make([]Announcement, 0, len(announcementsMap))
for _, student := range announcementsMap {
announcements = append(announcements, student)
}
return announcements, nil
}

func searchAnnouncements(subject string) ([]Announcement, error) {
// Read all announcements from the data source
announcements, err := readAnnouncements()
if err != nil {
return nil, err
}
// If the search subject is empty, return all announcements
if subject == "" {
return announcements, nil
}
// Filter announcements based on whether the subject contains the search term
var filteredAnnouncements []Announcement
lowerSubject := strings.ToLower(subject)
for _, announcement := range announcements {
if strings.Contains(strings.ToLower(announcement.Subject), lowerSubject) {
filteredAnnouncements = append(filteredAnnouncements, announcement)
}
}
return filteredAnnouncements, nil
}

func updateAnnouncement(announcementID string, updates map[string]interface{}, req *http.Request) error {
currentUser, err := GetCurrentUser(req)
if err != nil {
return fmt.Errorf("error getting current user: %v", err)
Expand All @@ -729,14 +764,15 @@ func updateAnnouncement(announcement Announcement, updates map[string]interface{
if !isAdmin(currentUser) {
return fmt.Errorf("unauthorized access: only admins can update this announcement")
}
ref := firebaseClient.NewRef("announcements/" + announcement.AnnouncementID)
ref := firebaseClient.NewRef("announcements/" + announcementID)
if err := ref.Update(context.TODO(), updates); err != nil {
return fmt.Errorf("error updating announcement: %v", err)
}
return ref.Update(context.TODO(), updates)
// return ref.Update(context.TODO(), updates)
return nil
}

func deleteAnnouncement(announcement Announcement, req *http.Request) error {
func deleteAnnouncement(announcementID string, req *http.Request) error {
currentUser, err := GetCurrentUser(req)
if err != nil {
return fmt.Errorf("error getting current user: %v", err)
Expand All @@ -745,7 +781,7 @@ func deleteAnnouncement(announcement Announcement, req *http.Request) error {
if !isAdmin(currentUser) {
return fmt.Errorf("unauthorized access: only admins can delete announcements")
}
ref := firebaseClient.NewRef("announcements/" + announcement.AnnouncementID)
ref := firebaseClient.NewRef("announcements/" + announcementID)
if err := ref.Delete(context.TODO()); err != nil {
return fmt.Errorf("error deleting announcement: %v", err)
}
Expand Down
5 changes: 2 additions & 3 deletions database_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
// "github.com/google/uuid"
)

// User struct to represent the base user with common fields
// User represent the base user with common fields
type User struct {
// ID uuid.UUID `json:"id"`
GoogleID string `json:"google_id"`
Expand All @@ -17,7 +17,7 @@ type User struct {
UpdatedAt time.Time `json:"updated_at"`
}

// Student struct for storing student information
// Student represents struct for storing student information
type Student struct {
User
Age int `json:"age"`
Expand Down Expand Up @@ -64,7 +64,6 @@ type Announcement struct {
}

// NewStudent creates a new Student instance
// func NewStudent(googleID, name, email, contactNumber, class, instructor, parentName, role string, age int, lessonCredits float32) Student {
func NewStudent(googleID, name, email, contactNumber, classID, parentID, role string, age int, lessonCredits float32) Student {
return Student{
User: User{
Expand Down
Loading
Loading