diff --git a/.gitignore b/.gitignore index d399d3f..258af4f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /uploads .env -/Example_code \ No newline at end of file +/tmp +/Example_code diff --git a/app/internal/handlers/admin_addresses.go b/app/internal/handlers/admin_addresses.go index e082d22..842e080 100644 --- a/app/internal/handlers/admin_addresses.go +++ b/app/internal/handlers/admin_addresses.go @@ -4,6 +4,7 @@ import ( "log" "net/http" "strconv" + "time" "github.com/patel-mann/poll-system/app/internal/models" "github.com/patel-mann/poll-system/app/internal/utils" @@ -262,12 +263,19 @@ func AssignAddressHandler(w http.ResponseWriter, r *http.Request) { userIDStr := r.FormValue("user_id") addressIDStr := r.FormValue("address_id") + appointmentDate := r.FormValue("appointment_date") + startTime := r.FormValue("time") if userIDStr == "" || addressIDStr == "" { http.Error(w, "User ID and Address ID are required", http.StatusBadRequest) return } + if appointmentDate == "" || startTime == "" { + http.Error(w, "Appointment date and start time are required", http.StatusBadRequest) + return + } + userID, err := strconv.Atoi(userIDStr) if err != nil { http.Error(w, "Invalid user ID", http.StatusBadRequest) @@ -280,6 +288,27 @@ func AssignAddressHandler(w http.ResponseWriter, r *http.Request) { return } + // Parse and validate the appointment date + parsedDate, err := time.Parse("2006-01-02", appointmentDate) + if err != nil { + http.Error(w, "Invalid appointment date format", http.StatusBadRequest) + return + } + + // Validate that the appointment date is not in the past + today := time.Now().Truncate(24 * time.Hour) + if parsedDate.Before(today) { + http.Error(w, "Appointment date cannot be in the past", http.StatusBadRequest) + return + } + + // Parse and validate the start time + _, err = time.Parse("15:04", startTime) + if err != nil { + http.Error(w, "Invalid start time format", http.StatusBadRequest) + return + } + // Verify the user exists and is associated with the current admin currentAdminID := r.Context().Value("user_id").(int) var userExists int @@ -314,11 +343,27 @@ func AssignAddressHandler(w http.ResponseWriter, r *http.Request) { return } - // Assign the address - create appointment + // Check if the user already has an appointment at the same date and time + var timeConflict int + err = models.DB.QueryRow(` + SELECT COUNT(*) FROM appointment + WHERE user_id = $1 AND appointment_date = $2 AND appointment_time = $3 + `, userID, appointmentDate, startTime).Scan(&timeConflict) + if err != nil { + log.Println("Time conflict check error:", err) + http.Error(w, "Database error", http.StatusInternalServerError) + return + } + if timeConflict > 0 { + http.Error(w, "User already has an appointment at this date and time", http.StatusBadRequest) + return + } + + // Assign the address - create appointment with specific date and time _, err = models.DB.Exec(` INSERT INTO appointment (user_id, address_id, appointment_date, appointment_time, created_at, updated_at) - VALUES ($1, $2, CURRENT_DATE, CURRENT_TIME, NOW(), NOW()) - `, userID, addressID) + VALUES ($1, $2, $3, $4, NOW(), NOW()) + `, userID, addressID, appointmentDate, startTime) if err != nil { log.Println("Assignment error:", err) http.Error(w, "Failed to assign address", http.StatusInternalServerError) diff --git a/app/internal/handlers/admin_dashboard.go b/app/internal/handlers/admin_dashboard.go index 1670258..eaca1b6 100644 --- a/app/internal/handlers/admin_dashboard.go +++ b/app/internal/handlers/admin_dashboard.go @@ -33,7 +33,7 @@ func AdminDashboardHandler(w http.ResponseWriter, r *http.Request) { // 2. Total donations from polls err = models.DB.QueryRow(` SELECT COALESCE(SUM(amount_donated), 0) - FROM poll; + FROM poll_responce; `).Scan(&totalDonations) if err != nil { log.Println("Donation query error:", err) diff --git a/app/internal/handlers/volunteer_address.go b/app/internal/handlers/volunteer_address.go index 3bded6c..68362b9 100644 --- a/app/internal/handlers/volunteer_address.go +++ b/app/internal/handlers/volunteer_address.go @@ -8,73 +8,103 @@ import ( "github.com/patel-mann/poll-system/app/internal/utils" ) - func VolunteerAppointmentHandler(w http.ResponseWriter, r *http.Request) { - // Fetch appointments joined with address info + // Fetch appointments joined with address info + currentUserID := models.GetCurrentUserID(w, r) + username, _ := models.GetCurrentUserName(r) - currentUserID := models.GetCurrentUserID(w,r) - username,_ := models.GetCurrentUserName(r) + rows, err := models.DB.Query(` + SELECT + a.sched_id, + a.user_id, + ad.address_id, + ad.address, + ad.latitude, + ad.longitude, + a.appointment_date, + a.appointment_time + FROM appointment a + JOIN address_database ad ON a.address_id = ad.address_id + WHERE a.user_id = $1 + `, currentUserID) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + defer rows.Close() - rows, err := models.DB.Query(` - SELECT - a.sched_id, - a.user_id, - ad.address, - ad.latitude, - ad.longitude, - a.appointment_date, - a.appointment_time - FROM appointment a - JOIN address_database ad ON a.address_id = ad.address_id - WHERE a.user_id = $1 - `, currentUserID) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - defer rows.Close() + // Struct to hold appointment + address info + type AppointmentWithAddress struct { + SchedID int + UserID int + AddressID int + Address string + Latitude float64 + Longitude float64 + AppointmentDate time.Time + AppointmentTime time.Time + HasPollResponse bool // New field to track poll status + PollButtonText string // New field for button text + PollButtonClass string // New field for button styling + } - // Struct to hold appointment + address info - type AppointmentWithAddress struct { - SchedID int - UserID int - Address string - Latitude float64 - Longitude float64 - AppointmentDate time.Time - AppointmentTime time.Time - } + var appointments []AppointmentWithAddress + for rows.Next() { + var a AppointmentWithAddress + if err := rows.Scan(&a.SchedID, &a.UserID, &a.AddressID, &a.Address, &a.Latitude, &a.Longitude, &a.AppointmentDate, &a.AppointmentTime); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // Check if poll response exists for this address + var pollResponseExists bool + err = models.DB.QueryRow(` + SELECT EXISTS( + SELECT 1 + FROM poll p + JOIN poll_response pr ON p.poll_id = pr.poll_id + WHERE p.address_id = $1 AND p.user_id = $2 + ) + `, a.AddressID, currentUserID).Scan(&pollResponseExists) + + if err != nil { + // If there's an error checking, default to no response + pollResponseExists = false + } + + // Set button properties based on poll response status + a.HasPollResponse = pollResponseExists + if pollResponseExists { + a.PollButtonText = "Poll Taken" + a.PollButtonClass = "px-3 py-1 bg-green-600 text-white text-sm rounded cursor-not-allowed" + } else { + a.PollButtonText = "Ask Poll" + a.PollButtonClass = "px-3 py-1 bg-blue-600 text-white text-sm hover:bg-blue-700 rounded" + } + + appointments = append(appointments, a) + } - var appointments []AppointmentWithAddress - for rows.Next() { - var a AppointmentWithAddress - if err := rows.Scan(&a.SchedID, &a.UserID, &a.Address, &a.Latitude, &a.Longitude, &a.AppointmentDate, &a.AppointmentTime); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - appointments = append(appointments, a) - } - role := r.Context().Value("user_role").(int) adminnav := false volunteernav := false - if role == 1{ + if role == 1 { adminnav = true volunteernav = false - }else{ + } else { adminnav = false volunteernav = true } - // Render template - utils.Render(w, "/appointment.html", map[string]interface{}{ - "Title": "My Profile", - "IsAuthenticated": true, - "ShowAdminNav": adminnav, // your existing variable - "ShowVolunteerNav": volunteernav, // your existing variable - "ActiveSection": "address", - "UserName": username, - "Appointments": appointments, // pass the fetched appointments - }) -} + // Render template + utils.Render(w, "/appointment.html", map[string]interface{}{ + "Title": "My Profile", + "IsAuthenticated": true, + "ShowAdminNav": adminnav, + "ShowVolunteerNav": volunteernav, + "ActiveSection": "address", + "UserName": username, + "Appointments": appointments, + }) +} \ No newline at end of file diff --git a/app/internal/handlers/volunteer_poll.go b/app/internal/handlers/volunteer_poll.go new file mode 100644 index 0000000..441e23e --- /dev/null +++ b/app/internal/handlers/volunteer_poll.go @@ -0,0 +1,138 @@ +package handlers + +import ( + "database/sql" + "fmt" + "log" + + "net/http" + "strconv" + + "github.com/patel-mann/poll-system/app/internal/models" + "github.com/patel-mann/poll-system/app/internal/utils" +) + +func PollHandler(w http.ResponseWriter, r *http.Request) { + username, _ := models.GetCurrentUserName(r) + + if r.Method == http.MethodGet { + addressID := r.URL.Query().Get("address_id") + if addressID == "" { + http.Error(w, "Address ID required", http.StatusBadRequest) + return + } + + // Get address details + var address string + var userID int + fmt.Print(addressID, userID) + err := models.DB.QueryRow(` + SELECT a.address, ap.user_id + FROM appointment AS ap + JOIN address_database a ON a.address_id = ap.address_id + WHERE ap.address_id = $1 + `, addressID).Scan(&address, &userID) + + if err != nil { + http.Error(w, "Address not found", http.StatusNotFound) + return + } + + // Check if poll already exists for this address + var pollID int + err = models.DB.QueryRow(` + SELECT poll_id + FROM poll + WHERE address_id = $1 AND user_id = $2 + `, addressID, userID).Scan(&pollID) + + // If no poll exists, create one + if err == sql.ErrNoRows { + err = models.DB.QueryRow(` + INSERT INTO poll (user_id, address_id, poll_title, poll_description, is_active) + VALUES ($1, $2, 'Door-to-Door Poll', 'Campaign polling questions', true) + RETURNING poll_id + `, userID, addressID).Scan(&pollID) + + if err != nil { + log.Printf("Failed to create poll: %v", err) + http.Error(w, "Failed to create poll", http.StatusInternalServerError) + return + } + } else if err != nil { + log.Printf("Database error: %v", err) + http.Error(w, "Database error", http.StatusInternalServerError) + return + } + + utils.Render(w, "volunteer/poll_form.html", map[string]interface{}{ + "Title": "Poll Questions", + "IsAuthenticated": true, + "ShowAdminNav": true, + "UserName": username, + "ActiveSection": "appointments", + "PollID": pollID, + "AddressID": addressID, + "Address": address, + "PageIcon": "fas fa-poll", + }) + return + } + + if r.Method == http.MethodPost { + err := r.ParseForm() + if err != nil { + http.Error(w, "Invalid form", http.StatusBadRequest) + return + } + + pollID := r.FormValue("poll_id") + postalCode := r.FormValue("postal_code") + question5Thoughts := r.FormValue("question5_thoughts") + + // Parse boolean values + var question1VotedBefore *bool + if val := r.FormValue("question1_voted_before"); val != "" { + if val == "true" { + b := true + question1VotedBefore = &b + } else if val == "false" { + b := false + question1VotedBefore = &b + } + } + + var question2VoteAgain *bool + if val := r.FormValue("question2_vote_again"); val != "" { + if val == "true" { + b := true + question2VoteAgain = &b + } else if val == "false" { + b := false + question2VoteAgain = &b + } + } + + // Parse integer values + question3LawnSigns, _ := strconv.Atoi(r.FormValue("question3_lawn_signs")) + question4BannerSigns, _ := strconv.Atoi(r.FormValue("question4_banner_signs")) + + // Insert poll response + _, err = models.DB.Exec(` + INSERT INTO poll_response ( + poll_id, respondent_postal_code, question1_voted_before, + question2_vote_again, question3_lawn_signs, question4_banner_signs, + question5_thoughts + ) VALUES ($1, $2, $3, $4, $5, $6, $7) + `, pollID, postalCode, question1VotedBefore, question2VoteAgain, + question3LawnSigns, question4BannerSigns, question5Thoughts) + + 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) + } +} \ No newline at end of file diff --git a/app/internal/handlers/volunteer_posts.go b/app/internal/handlers/volunteer_posts.go index d9ee1e9..0cfa9a9 100644 --- a/app/internal/handlers/volunteer_posts.go +++ b/app/internal/handlers/volunteer_posts.go @@ -1,5 +1,4 @@ // Add this to your handlers package (create volunteer_posts.go or add to existing file) - package handlers import ( @@ -12,7 +11,18 @@ import ( "github.com/patel-mann/poll-system/app/internal/utils" ) -// VolunteerPostsHandler - Read-only posts view for volunteers +type VolunteerStatistics struct { + AppointmentsToday int + AppointmentsThisWeek int + TotalAppointments int + PollsCompleted int + PollsRemaining int + LawnSignsRequested int + BannerSignsRequested int + PollCompletionPercent int +} + +// VolunteerPostsHandler - Dashboard view for volunteers with posts and statistics func VolunteerPostsHandler(w http.ResponseWriter, r *http.Request) { // Only allow GET requests for volunteers if r.Method != http.MethodGet { @@ -23,7 +33,7 @@ func VolunteerPostsHandler(w http.ResponseWriter, r *http.Request) { // Get user info from context role := r.Context().Value("user_role").(int) CurrentUserID := models.GetCurrentUserID(w, r) - username,_ := models.GetCurrentUserName(r) + username, _ := models.GetCurrentUserName(r) // Fetch posts from database rows, err := models.DB.Query(` @@ -34,7 +44,7 @@ func VolunteerPostsHandler(w http.ResponseWriter, r *http.Request) { JOIN admin_volunteers x ON u.user_id = x.admin_id WHERE x.volunteer_id = $1 ORDER BY p.created_at DESC - `,CurrentUserID) + `, CurrentUserID) if err != nil { fmt.Printf("Database query error: %v\n", err) http.Error(w, "Failed to fetch posts", http.StatusInternalServerError) @@ -60,20 +70,107 @@ func VolunteerPostsHandler(w http.ResponseWriter, r *http.Request) { } } + // Get volunteer statistics + stats, err := getVolunteerStatistics(CurrentUserID) + if err != nil { + fmt.Printf("Failed to fetch statistics: %v\n", err) + // Continue with empty stats rather than failing + stats = &VolunteerStatistics{} + } + // Get navigation flags showAdminNav, showVolunteerNav := getNavFlags(role) fmt.Printf("Volunteer viewing %d posts\n", len(posts)) - + utils.Render(w, "dashboard/volunteer_dashboard.html", map[string]interface{}{ - "Title": "Community Posts", + "Title": "Volunteer Dashboard", "IsAuthenticated": true, "ShowAdminNav": showAdminNav, "ShowVolunteerNav": showVolunteerNav, - "UserName": username, + "UserName": username, "Posts": posts, - "ActiveSection": "posts", - "IsVolunteer": true, // Flag to indicate this is volunteer view + "Statistics": stats, + "ActiveSection": "dashboard", + "IsVolunteer": true, }) } +func getVolunteerStatistics(userID int) (*VolunteerStatistics, error) { + stats := &VolunteerStatistics{} + today := time.Now().Format("2006-01-02") + + // Get start of current week (Monday) + now := time.Now() + weekday := now.Weekday() + if weekday == time.Sunday { + weekday = 7 + } + weekStart := now.AddDate(0, 0, -int(weekday)+1).Format("2006-01-02") + + // Appointments today + err := models.DB.QueryRow(` + SELECT COUNT(*) + FROM appointment + WHERE user_id = $1 AND DATE(appointment_date) = $2 + `, userID, today).Scan(&stats.AppointmentsToday) + if err != nil { + return nil, err + } + + // Appointments this week + err = models.DB.QueryRow(` + SELECT COUNT(*) + FROM appointment + WHERE user_id = $1 AND DATE(appointment_date) >= $2 AND DATE(appointment_date) <= $3 + `, userID, weekStart, today).Scan(&stats.AppointmentsThisWeek) + if err != nil { + return nil, err + } + + // Total appointments + err = models.DB.QueryRow(` + SELECT COUNT(*) + FROM appointment + WHERE user_id = $1 + `, userID).Scan(&stats.TotalAppointments) + if err != nil { + return nil, err + } + + // Polls completed + err = models.DB.QueryRow(` + SELECT COUNT(DISTINCT pr.poll_response_id) + FROM poll p + JOIN poll_response pr ON p.poll_id = pr.poll_id + WHERE p.user_id = $1 + `, userID).Scan(&stats.PollsCompleted) + if err != nil { + return nil, err + } + + // Polls remaining (appointments without poll responses) + stats.PollsRemaining = stats.TotalAppointments - stats.PollsCompleted + + // Calculate completion percentage + if stats.TotalAppointments > 0 { + stats.PollCompletionPercent = (stats.PollsCompleted * 100) / stats.TotalAppointments + } else { + stats.PollCompletionPercent = 0 + } + + // Signs requested + err = models.DB.QueryRow(` + SELECT + COALESCE(SUM(pr.question3_lawn_signs), 0), + COALESCE(SUM(pr.question4_banner_signs), 0) + FROM poll p + JOIN poll_response pr ON p.poll_id = pr.poll_id + WHERE p.user_id = $1 + `, userID).Scan(&stats.LawnSignsRequested, &stats.BannerSignsRequested) + if err != nil { + return nil, err + } + + return stats, nil +} \ No newline at end of file diff --git a/app/internal/models/token.go b/app/internal/models/token.go index 7fe780c..ab1b3e9 100644 --- a/app/internal/models/token.go +++ b/app/internal/models/token.go @@ -18,7 +18,7 @@ func GetCurrentUserName(r *http.Request) (string, error) { var currentUserName string err := DB.QueryRow(` - SELECT first_name || ' ' || last_name + SELECT first_name FROM users WHERE user_id = $1 `, currentUserID).Scan(¤tUserName) diff --git a/app/internal/templates/address/address.html b/app/internal/templates/address/address.html index 51e2904..4d2dd49 100644 --- a/app/internal/templates/address/address.html +++ b/app/internal/templates/address/address.html @@ -100,7 +100,7 @@ > Validated Address - Cordinates + Coordinates Assigned User Appointment Assign @@ -158,7 +158,7 @@ {{ else }} @@ -187,7 +187,7 @@ {{ else }} - + No addresses found @@ -201,37 +201,125 @@ id="assignModal" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center z-50" > -
-

Assign Address

-
+
+ +
+

Assign Address

+ +
+ + + - - -
+ + +
+
+ + Selected Address: +
+
+ None selected +
+ + +
+ + +
+ + +
+ + +
+ + +
+ +
+ + +
+
+
+ + +
+
+ + Duration: 0 minutes +
+
+ + +
@@ -239,26 +327,105 @@
+ + + {{ end }} diff --git a/app/internal/templates/address_assigned.html b/app/internal/templates/address_assigned.html deleted file mode 100644 index 4b64633..0000000 --- a/app/internal/templates/address_assigned.html +++ /dev/null @@ -1,38 +0,0 @@ -{{ define "content" }} -
-

Assigned Addresses

- - - - - - - - - - - - - - - - {{range .AssignedList}} - - - - - - - - - - - {{end}} - -
IDAddressAssignedVolunteerEmailPhoneAppointment DateAppointment Time
{{.AddressID}} - {{.Address}} {{.StreetName}} {{.StreetType}} {{.StreetQuadrant}} - - {{if .Assigned}}✅ Yes{{else}}❌ No{{end}} - {{.UserName}}{{.UserEmail}}{{.UserPhone}}{{.AppointmentDate}}{{.AppointmentTime}}
-
-{{ end }} diff --git a/app/internal/templates/appointment.html b/app/internal/templates/appointment.html index 612778e..3528b70 100644 --- a/app/internal/templates/appointment.html +++ b/app/internal/templates/appointment.html @@ -8,7 +8,7 @@ - Appointments + Appointments
@@ -42,7 +42,7 @@ class="text-left text-gray-700 font-medium border-b border-gray-200" > Address - Cordinated + Coordinates Appointment Date Appointment Time Poll Question @@ -68,11 +68,16 @@ {{ .AppointmentTime.Format "15:04" }} - + {{ .PollButtonText }} + + {{ end }} {{ else }} diff --git a/app/internal/templates/dashboard/volunteer_dashboard.html b/app/internal/templates/dashboard/volunteer_dashboard.html index 61fe5fe..329533b 100644 --- a/app/internal/templates/dashboard/volunteer_dashboard.html +++ b/app/internal/templates/dashboard/volunteer_dashboard.html @@ -1,122 +1,286 @@ {{ define "content" }} -
- -
-

Community

-
- - -
- - {{range .Posts}} -
- -
-
-
- {{slice .AuthorName 0 1}} -
-
-
-

{{.AuthorName}}

-

- {{.CreatedAt.Format "Jan 2, 2006"}} -

+
+ +
+
+
+
+ + Volunteer Dashboard
- - - {{if .ImageURL}} -
- Post image -
- {{end}} - - -
-
- - -
-
- - - {{if .Content}} -
-

- {{.AuthorName}} {{.Content}} -

-
- {{end}} -
- {{else}} -
-
- - - -

No posts yet

-

- Be the first to share something with the community! -

+ +
+
+ + +
+
+ +
+ {{ if .Posts }}{{range .Posts}} +
+ +
+
+
+ {{slice .AuthorName 0 1}} +
+
+
+

{{.AuthorName}}

+

+ {{.CreatedAt.Format "Jan 2, 2006"}} +

+
+
+ + + {{if .ImageURL}} +
+ Post image +
+ {{end}} + + + {{if .Content}} +
+

+ {{.AuthorName}} {{.Content}} +

+
+ {{end}} +
+ {{else}} +
+
+ + + +

No posts yet

+

+ Be the first to share something with the community! +

+
+
+ {{ end }} {{ else }} +
+
+
+ +
+

No posts yet

+

+ Check back later for updates from your team +

+
+
+ {{ end }} +
+ + +
+ +
+
+

+ Today's Overview +

+
+
+
+
+ +
+ Appointments Today +
+ + {{ .Statistics.AppointmentsToday }} + +
+ +
+
+
+ +
+ This Week +
+ + {{ .Statistics.AppointmentsThisWeek }} + +
+
+
+
+ + +
+
+

+ Polling Progress +

+
+
+
+
+ +
+ Polls Completed +
+ + {{ .Statistics.PollsCompleted }} + +
+ +
+
+
+ +
+ Polls Remaining +
+ + {{ .Statistics.PollsRemaining }} + +
+ + + {{ if gt .Statistics.TotalAppointments 0 }} +
+
+ Progress + {{ .Statistics.PollsCompleted }}/{{ + .Statistics.TotalAppointments }} +
+
+
+
+
+ {{ end }} +
+
+
+ + +
+
+

+ Signs Requested +

+
+
+
+
+ +
+ Lawn Signs +
+ + {{ .Statistics.LawnSignsRequested }} + +
+ +
+
+
+ +
+ Banner Signs +
+ + {{ .Statistics.BannerSignsRequested }} + +
+
+
+
+ + +
- {{end}}
{{ end }} diff --git a/app/internal/templates/layout.html b/app/internal/templates/layout.html index b49864b..1a3ea8b 100644 --- a/app/internal/templates/layout.html +++ b/app/internal/templates/layout.html @@ -31,7 +31,7 @@ {{.UserName}} -
+
{{slice .UserName 0 1}}
@@ -52,8 +52,8 @@
- Welcome, {{.UserName}} -
+ Hi, {{.UserName}} +
{{slice .UserName 0 1}}
diff --git a/app/internal/templates/volunteer/poll_form.html b/app/internal/templates/volunteer/poll_form.html new file mode 100644 index 0000000..ad6a9fb --- /dev/null +++ b/app/internal/templates/volunteer/poll_form.html @@ -0,0 +1,197 @@ +{{ define "content" }} +
+ + + + +
+
+
+
+

+ Campaign Poll Questions +

+

Address: {{ .Address }}

+
+ +
+ + + +
+ + +
+ + +
+ +
+ + +
+
+ + +
+ +
+ + +
+
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + Cancel + + +
+
+
+
+
+
+ + +{{ end }} diff --git a/app/main.go b/app/main.go index 3d40f6b..e1a7036 100644 --- a/app/main.go +++ b/app/main.go @@ -1,14 +1,10 @@ -// Add this debugging code to your main.go - package main import ( "context" - "fmt" "log" "net/http" "os" - "path/filepath" "github.com/golang-jwt/jwt/v5" "github.com/joho/godotenv" @@ -20,30 +16,10 @@ import ( _ "github.com/lib/pq" // use PostgreSQL ) -// Custom file server with logging -func loggingFileServer(dir string) http.Handler { - fs := http.FileServer(http.Dir(dir)) - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // Log the request - log.Printf("File request: %s", r.URL.Path) - - // Check if file exists - filePath := filepath.Join(dir, r.URL.Path) - if _, err := os.Stat(filePath); os.IsNotExist(err) { - log.Printf("File not found: %s", filePath) - http.NotFound(w, r) - return - } - - log.Printf("Serving file: %s", filePath) - fs.ServeHTTP(w, r) - }) -} - // Helper function to determine navigation visibility based on role func getNavFlags(role int) (bool, bool, bool) { showAdminNav := role == 1 // Admin role - showLeaderNav := role == 2 // Volunteer role + showLeaderNav := role == 2 // Team Leader role showVolunteerNav := role == 3 // Volunteer role return showAdminNav, showVolunteerNav, showLeaderNav } @@ -70,18 +46,14 @@ func createTemplateData(title, activeSection string, role int, isAuthenticated b } func authMiddleware(next http.HandlerFunc) http.HandlerFunc { - - err := godotenv.Load() // or specify path: godotenv.Load("/path/to/.env") + err := godotenv.Load() if err != nil { log.Fatalf("Error loading .env file: %v", err) } - // Get individual components from environment variables jwtSecret := os.Getenv("JWT_SECRET") var jwtKey = []byte(jwtSecret) - - return func(w http.ResponseWriter, r *http.Request) { cookie, err := r.Cookie("session") if err != nil { @@ -124,7 +96,6 @@ func volunteerMiddleware(next http.HandlerFunc) http.HandlerFunc { return authMiddleware(func(w http.ResponseWriter, r *http.Request) { role, ok := r.Context().Value("user_role").(int) if !ok || (role != 3 && role != 2) { - fmt.Printf("Access denied: role %d not allowed\n", role) // Debug log http.Redirect(w, r, "/", http.StatusSeeOther) return } @@ -132,36 +103,29 @@ func volunteerMiddleware(next http.HandlerFunc) http.HandlerFunc { }) } - - -// Updated handler functions using the helper func schedualHandler(w http.ResponseWriter, r *http.Request) { role := r.Context().Value("user_role").(int) - // currentUserID := r.Context().Value("user_id").(int) - data := createTemplateData("My Schedule", "schedual", role, true, nil) utils.Render(w, "Schedual/schedual.html", data) - } func HomeHandler(w http.ResponseWriter, r *http.Request) { utils.Render(w, "dashboard/dashboard.html", map[string]interface{}{ - "Title": "Admin Dashboard", - "IsAuthenticated": false, - "ActiveSection": "dashboard", - }) + "Title": "Admin Dashboard", + "IsAuthenticated": false, + "ActiveSection": "dashboard", + }) } - func main() { models.InitDB() - // Static file servers with logging + // Static file servers fs := http.FileServer(http.Dir("static")) http.Handle("/static/", http.StripPrefix("/static/", fs)) - // Use logging file server for uploads - http.Handle("/uploads/", http.StripPrefix("/uploads/", loggingFileServer("uploads"))) + uploadsFs := http.FileServer(http.Dir("uploads")) + http.Handle("/uploads/", http.StripPrefix("/uploads/", uploadsFs)) // Public HTML Routes http.HandleFunc("/", HomeHandler) @@ -187,17 +151,16 @@ func main() { http.HandleFunc("/assign_address", adminMiddleware(handlers.AssignAddressHandler)) http.HandleFunc("/remove_assigned_address", adminMiddleware(handlers.RemoveAssignedAddressHandler)) - - http.HandleFunc("/posts", adminMiddleware(handlers.PostsHandler)) //--- Volunteer-only routes http.HandleFunc("/volunteer/dashboard", volunteerMiddleware(handlers.VolunteerPostsHandler)) http.HandleFunc("/volunteer/Addresses", volunteerMiddleware(handlers.VolunteerAppointmentHandler)) - http.HandleFunc("/schedual", volunteerMiddleware(schedualHandler)) + // Poll routes (volunteer only) + http.HandleFunc("/poll", volunteerMiddleware(handlers.PollHandler)) log.Println("Server started on localhost:8080") log.Fatal(http.ListenAndServe("0.0.0.0:8080", nil)) -} +} \ No newline at end of file diff --git a/app/tmp/build-errors.log b/app/tmp/build-errors.log index cd8306b..f805f79 100644 --- a/app/tmp/build-errors.log +++ b/app/tmp/build-errors.log @@ -1 +1 @@ -exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1 \ No newline at end of file +exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1 \ No newline at end of file diff --git a/app/tmp/main b/app/tmp/main index afb2233..d60023b 100755 Binary files a/app/tmp/main and b/app/tmp/main differ