Skip to content

Commit

Permalink
Search/Edit/Create Announcements (#37)
Browse files Browse the repository at this point in the history
* changing the stuff to local again weeee

* Added announcements HTMLs

* Added announcements nav bar

* added more announcements crud for list

* added comments

* added announcements in admin handler

* added edit announcement

* added create announcement

* added announcement nav bar, removed users nav bar

* changed update announcement to use announcementid

* search/edit/create announcement works

* cleaned up commetns

* cleaned up unused code

* fixed response.json error

* cleaned up code

* prepping for pull request

* prep pt2

* fixed search not working

* cleaned up

* fixed search and edit announcements
  • Loading branch information
APandamonium1 authored Aug 11, 2024
1 parent ba4a2ca commit 1cf632b
Show file tree
Hide file tree
Showing 19 changed files with 562 additions and 101 deletions.
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

0 comments on commit 1cf632b

Please sign in to comment.