feat: added a side bar

This commit is contained in:
Mann Patel
2025-09-05 15:39:06 -06:00
parent a5bdc27de0
commit 05001a53e0
28 changed files with 1631 additions and 1655 deletions

View File

@@ -208,7 +208,7 @@ func AddressHandler(w http.ResponseWriter, r *http.Request) {
PageNumbers: pageNumbers,
}
utils.Render(w, "address/address.html", map[string]interface{}{
utils.Render(w, "address.html", map[string]interface{}{
"Title": "Addresses",
"IsAuthenticated": true,
"ShowAdminNav": true,

View File

@@ -67,7 +67,7 @@ func AdminDashboardHandler(w http.ResponseWriter, r *http.Request) {
housesLeftPercent = 0 // Set default value on error
}
utils.Render(w, "dashboard/dashboard.html", map[string]interface{}{
utils.Render(w, "dashboard.html", map[string]interface{}{
"Title": "Admin Dashboard",
"IsAuthenticated": true,
"VolunteerCount": volunteerCount,

View File

@@ -75,7 +75,7 @@ func ReportsHandler(w http.ResponseWriter, r *http.Request) {
"ShowAdminNav": role == 1,
"ShowVolunteerNav": role != 1,
"UserName": username,
"ActiveSection": "reports",
"ActiveSection": "reports",
"Category": category,
"ReportID": reportID,
"DateFrom": dateFrom,
@@ -177,20 +177,23 @@ func getAllReportDefinitions() map[string][]ReportDefinition {
return map[string][]ReportDefinition{
"users": {
{
ID: "users_by_role",
Name: "Users by Role",
ID: "volunteer_participation_rate", // get all the appointment(done, notdone, total) poll(done, not doen, total)
Name: "Volunteer participation rate",
Description: "Count of users grouped by their role",
SQL: `SELECT
CASE
WHEN role_id = 1 THEN 'Admin'
WHEN role_id = 2 THEN 'Volunteer'
ELSE 'Unknown'
END as role,
COUNT(*) as user_count,
COUNT(CASE WHEN created_at >= ?1 THEN 1 END) as new_this_period
FROM users
GROUP BY role_id
ORDER BY role_id`,
u.user_id,
u.first_name,
u.last_name,
COUNT(p.poll_id) AS total_polls,
COUNT(a.user_id) AS total_appointments,
case
WHEN COUNT(a.user_id) = 0 THEN NULL -- avoid division by zero
ELSE ROUND(CAST(COUNT(p.poll_id) AS numeric) / COUNT(a.user_id), 2)
END AS poll_to_appointment_rate
from users u
LEFT JOIN poll p ON u.user_id = p.user_id
LEFT JOIN appointment a ON u.user_id = a.user_id
GROUP BY u.user_id, u.first_name, u.last_name;`,
},
{
ID: "volunteer_activity",

View File

@@ -81,7 +81,7 @@ func TeamBuilderHandler(w http.ResponseWriter, r *http.Request) {
unassignedVolunteers = append(unassignedVolunteers, vol)
}
utils.Render(w, "volunteer/team_builder.html", map[string]interface{}{
utils.Render(w, "team_builder.html", map[string]interface{}{
"Title": "Team Builder",
"IsAuthenticated": true,
"ShowAdminNav": true,

View File

@@ -39,7 +39,7 @@ func VolunteerHandler(w http.ResponseWriter, r *http.Request) {
user = append(user, b)
}
utils.Render(w, "volunteer/volunteer.html", map[string]interface{}{
utils.Render(w, "volunteer.html", map[string]interface{}{
"Title": "Assigned Volunteers",
"IsAuthenticated": true,
"ShowAdminNav": true,
@@ -66,7 +66,7 @@ func EditVolunteerHandler(w http.ResponseWriter, r *http.Request) {
return
}
utils.Render(w, "volunteer/edit_volunteer.html", map[string]interface{}{
utils.Render(w, "edit_volunteer.html", map[string]interface{}{
"Title": "Edit Volunteer",
"IsAuthenticated": true,
"ShowAdminNav": true,

View File

@@ -30,29 +30,12 @@ func getDefaultRedirectURL(role int) string {
}
}
// Helper function to render error pages with consistent data
func renderLoginError(w http.ResponseWriter, errorMsg string) {
utils.Render(w, "login.html", map[string]interface{}{
"Error": errorMsg,
"Title": "Login",
"IsAuthenticated": false,
})
}
func renderRegisterError(w http.ResponseWriter, errorMsg string) {
utils.Render(w, "register.html", map[string]interface{}{
"Error": errorMsg,
"Title": "Register",
"IsAuthenticated": false,
})
}
// Helper function to create and sign JWT token
func createJWTToken(userID, role int) (string, time.Time, error) {
err := godotenv.Load() // or specify path: godotenv.Load("/path/to/.env")
if err != nil {
log.Fatalf("Error loading .env file: %v", err)
log.Fatalf("Error loading .env file: %v", err)
}
// Get individual components from environment variables
@@ -60,7 +43,6 @@ func createJWTToken(userID, role int) (string, time.Time, error) {
var jwtKey = []byte(jwtSecret)
expirationTime := time.Now().Add(12 * time.Hour)
claims := &models.Claims{
UserID: userID,
@@ -113,7 +95,7 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) {
// Input validation
if email == "" || password == "" {
http.Redirect(w, r, "/?error=EmailAndPasswordRequired", http.StatusSeeOther)
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
@@ -130,7 +112,7 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) {
if err != nil {
log.Printf("Login failed for email %s: %v", email, err)
http.Redirect(w, r, "/?error=InvalidCredentials", http.StatusSeeOther)
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
@@ -138,7 +120,7 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) {
err = bcrypt.CompareHashAndPassword([]byte(storedHash), []byte(password))
if err != nil {
log.Printf("Password verification failed for user ID %d", userID)
http.Redirect(w, r, "/?error=InvalidCredentials", http.StatusSeeOther)
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
@@ -146,7 +128,7 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) {
tokenString, expirationTime, err := createJWTToken(userID, role)
if err != nil {
log.Printf("JWT token creation failed for user ID %d: %v", userID, err)
http.Redirect(w, r, "/?error=InternalError", http.StatusSeeOther)
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
@@ -159,7 +141,6 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, redirectURL, http.StatusSeeOther)
}
func RegisterHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
utils.Render(w, "layout.html", map[string]interface{}{
@@ -179,7 +160,7 @@ func RegisterHandler(w http.ResponseWriter, r *http.Request) {
// Input validation
if firstName == "" || lastName == "" || email == "" || password == "" || role == "" {
renderRegisterError(w, "All fields are required")
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
@@ -187,21 +168,21 @@ func RegisterHandler(w http.ResponseWriter, r *http.Request) {
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
log.Printf("Password hashing failed: %v", err)
http.Error(w, "Internal server error", http.StatusInternalServerError)
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
// Convert role to int
roleID, err := strconv.Atoi(role)
if err != nil {
renderRegisterError(w, "Invalid role")
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
var adminID int
if roleID == 3 { // volunteer
if adminCode == "" {
renderRegisterError(w, "Admin code is required for volunteers")
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
@@ -209,11 +190,11 @@ func RegisterHandler(w http.ResponseWriter, r *http.Request) {
err = models.DB.QueryRow(`SELECT user_id FROM users WHERE role_id = 1 AND admin_code = $1`, adminCode).Scan(&adminID)
if err != nil {
if err == sql.ErrNoRows {
renderRegisterError(w, "Invalid admin code")
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
log.Printf("DB error checking admin code: %v", err)
http.Error(w, "Internal server error", http.StatusInternalServerError)
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
}
@@ -227,7 +208,7 @@ func RegisterHandler(w http.ResponseWriter, r *http.Request) {
`, firstName, lastName, email, phone, string(hashedPassword), roleID).Scan(&userID)
if err != nil {
log.Printf("User registration failed: %v", err)
renderRegisterError(w, "Could not create account. Email might already be in use.")
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
@@ -239,7 +220,7 @@ func RegisterHandler(w http.ResponseWriter, r *http.Request) {
`, adminID, userID)
if err != nil {
log.Printf("Failed to link volunteer to admin: %v", err)
http.Error(w, "Internal server error", http.StatusInternalServerError)
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
}
@@ -248,9 +229,7 @@ func RegisterHandler(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/", http.StatusSeeOther)
}
func LogoutHandler(w http.ResponseWriter, r *http.Request) {
clearSessionCookie(w)
http.Redirect(w, r, "/", http.StatusSeeOther)
}
}

View File

@@ -47,7 +47,7 @@ func ProfileHandler(w http.ResponseWriter, r *http.Request) {
volunteernav = true
}
utils.Render(w, "profile/profile.html", map[string]interface{}{
utils.Render(w, "profile.html", map[string]interface{}{
"Title": "My Profile",
"IsAuthenticated": true,
"ShowAdminNav": adminnav,

View File

@@ -98,7 +98,7 @@ func VolunteerAppointmentHandler(w http.ResponseWriter, r *http.Request) {
}
// Render template
utils.Render(w, "/appointment.html", map[string]interface{}{
utils.Render(w, "appointment.html", map[string]interface{}{
"Title": "My Profile",
"IsAuthenticated": true,
"ShowAdminNav": adminnav,

View File

@@ -124,7 +124,7 @@ func VolunteerPostsHandler(w http.ResponseWriter, r *http.Request) {
fmt.Printf("Volunteer viewing %d posts\n", len(posts))
utils.Render(w, "dashboard/volunteer_dashboard.html", map[string]interface{}{
utils.Render(w, "volunteer_dashboard.html", map[string]interface{}{
"Title": "Volunteer Dashboard",
"IsAuthenticated": true,
"ShowAdminNav": showAdminNav,

View File

@@ -65,7 +65,7 @@ func PollHandler(w http.ResponseWriter, r *http.Request) {
return
}
utils.Render(w, "volunteer/poll_form.html", map[string]interface{}{
utils.Render(w, "poll_form.html", map[string]interface{}{
"Title": "Poll Questions",
"IsAuthenticated": true,
"ShowAdminNav": true,
@@ -120,7 +120,6 @@ func PollHandler(w http.ResponseWriter, r *http.Request) {
}
}
// Insert poll response
_, err = models.DB.Exec(`
INSERT INTO poll_response (
@@ -135,6 +134,22 @@ func PollHandler(w http.ResponseWriter, r *http.Request) {
fmt.Print(err)
http.Error(w, "Failed to save poll response", http.StatusInternalServerError)
return
}else{
_, err := models.DB.Exec(`
UPDATE address_database
SET visited_validated = true
WHERE address_id IN (
SELECT address_id
FROM poll
WHERE poll_id = $1
)
`, pollID)
if err != nil {
fmt.Print(err)
http.Error(w, "Failed to save poll response", http.StatusInternalServerError)
return
}
}
http.Redirect(w, r, "/volunteer/Addresses", http.StatusSeeOther)

View File

@@ -0,0 +1,11 @@
package handlers
import (
"fmt"
"net/http"
)
func VolunteerSchedualHandler(w *http.ResponseWriter, r http.Request) {
fmt.Print("Not Implementated Yet!!!")
}