From 9dd24e71e70b0de4a5d6a907c288670027d0fcd2 Mon Sep 17 00:00:00 2001 From: Mann Patel <130435633+Patel-Mann@users.noreply.github.com> Date: Thu, 28 Aug 2025 00:15:10 -0600 Subject: [PATCH] updated layout and volunteer change the ui and now able to remove assigned addresses from volunteer --- app/internal/handlers/admin_addresses.go | 67 ++- app/internal/handlers/admin_apointment.go | 83 ---- app/internal/handlers/admin_voluteers.go | 3 + app/internal/models/structs.go | 2 +- app/internal/templates/address/address.html | 21 + app/internal/templates/layout.html | 392 ++++++++---------- app/internal/templates/posts.html | 27 +- app/internal/templates/profile/profile.html | 114 +---- .../templates/volunteer/edit_volunteer.html | 125 +----- app/main.go | 2 + app/static/favicon.ico | Bin 0 -> 5238 bytes app/static/feature-mobile1.jpg | Bin 0 -> 46061 bytes app/static/feature-mobile2.jpg | Bin 0 -> 41041 bytes app/static/feature-mobile3.jpg | Bin 0 -> 50694 bytes app/static/feature-mobile4.jpg | Bin 0 -> 656478 bytes app/static/icon-512.png | Bin 0 -> 16397 bytes app/tmp/build-errors.log | 2 +- app/tmp/main | Bin 13240432 -> 13249056 bytes app/uploads/3_1756358325869790000.jpg | Bin 0 -> 52116 bytes app/uploads/3_1756358508202969000.jpg | Bin 0 -> 44133 bytes 20 files changed, 302 insertions(+), 536 deletions(-) delete mode 100644 app/internal/handlers/admin_apointment.go create mode 100644 app/static/favicon.ico create mode 100644 app/static/feature-mobile1.jpg create mode 100644 app/static/feature-mobile2.jpg create mode 100644 app/static/feature-mobile3.jpg create mode 100644 app/static/feature-mobile4.jpg create mode 100644 app/static/icon-512.png create mode 100644 app/uploads/3_1756358325869790000.jpg create mode 100644 app/uploads/3_1756358508202969000.jpg diff --git a/app/internal/handlers/admin_addresses.go b/app/internal/handlers/admin_addresses.go index 342b6c8..e082d22 100644 --- a/app/internal/handlers/admin_addresses.go +++ b/app/internal/handlers/admin_addresses.go @@ -34,6 +34,7 @@ type PageNumber struct { // AddressWithDetails extends AddressDatabase with appointment and user info type AddressWithDetails struct { models.AddressDatabase + UserID *int UserName string UserEmail string AppointmentDate string @@ -97,6 +98,7 @@ func AddressHandler(w http.ResponseWriter, r *http.Request) { WHEN ap.sched_id IS NOT NULL THEN true ELSE false END as assigned, + ap.user_id, COALESCE(u.first_name || ' ' || u.last_name, '') as user_name, COALESCE(u.email, '') as user_email, COALESCE(ap.appointment_date::text, '') as appointment_date, @@ -133,6 +135,7 @@ func AddressHandler(w http.ResponseWriter, r *http.Request) { &a.CreatedAt, &a.UpdatedAt, &a.Assigned, + &a.UserID, &a.UserName, &a.UserEmail, &a.AppointmentDate, @@ -324,4 +327,66 @@ func AssignAddressHandler(w http.ResponseWriter, r *http.Request) { // Redirect back to addresses page with success http.Redirect(w, r, "/addresses?success=assigned", http.StatusSeeOther) -} \ No newline at end of file +} + + +func RemoveAssignedAddressHandler(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Redirect(w, r, "/addresses", http.StatusSeeOther) + return + } + + if err := r.ParseForm(); err != nil { + http.Error(w, "Invalid form", http.StatusBadRequest) + return + } + + userIDStr := r.FormValue("user_id") + addressIDStr := r.FormValue("address_id") + + if userIDStr == "" || addressIDStr == "" { + http.Error(w, "User ID and Address ID are required", http.StatusBadRequest) + return + } + + userID, err := strconv.Atoi(userIDStr) + if err != nil { + http.Error(w, "Invalid user ID", http.StatusBadRequest) + return + } + + addressID, err := strconv.Atoi(addressIDStr) + if err != nil { + http.Error(w, "Invalid address ID", http.StatusBadRequest) + return + } + + // Verify the user is managed by current admin + currentAdminID := r.Context().Value("user_id").(int) + var userExists int + err = models.DB.QueryRow(` + SELECT COUNT(*) + FROM admin_volunteers av + JOIN appointment ap ON av.volunteer_id = ap.user_id + WHERE av.admin_id = $1 AND ap.user_id = $2 AND ap.address_id = $3 + `, currentAdminID, userID, addressID).Scan(&userExists) + if err != nil { + log.Println("Verification error:", err) + http.Error(w, "Database error", http.StatusInternalServerError) + return + } + if userExists == 0 { + http.Error(w, "Unauthorized removal", http.StatusForbidden) + return + } + + // Remove volunteer assignment + _, err = models.DB.Exec(`DELETE FROM appointment WHERE user_id = $1 AND address_id = $2`, userID, addressID) + if err != nil { + log.Println("Remove assignment error:", err) + http.Error(w, "Failed to remove assignment", http.StatusInternalServerError) + return + } + + http.Redirect(w, r, "/addresses?success=removed", http.StatusSeeOther) +} diff --git a/app/internal/handlers/admin_apointment.go b/app/internal/handlers/admin_apointment.go deleted file mode 100644 index 33b18f9..0000000 --- a/app/internal/handlers/admin_apointment.go +++ /dev/null @@ -1,83 +0,0 @@ -package handlers - -import ( - "log" - "net/http" - - "github.com/patel-mann/poll-system/app/internal/models" - "github.com/patel-mann/poll-system/app/internal/utils" -) - -type AssignedAddress struct { - AddressID int - Address string - StreetName string - StreetType string - StreetQuadrant string - HouseNumber string - HouseAlpha *string - Longitude float64 - Latitude float64 - VisitedValidated bool - CreatedAt string - UpdatedAt string - Assigned bool - UserName string - UserEmail string - UserPhone string - AppointmentDate *string - AppointmentTime *string -} - -func AssignedAddressesHandler(w http.ResponseWriter, r *http.Request) { - username,_ := models.GetCurrentUserName(r) - - rows, err := models.DB.Query(` - SELECT - a.address_id, a.address, a.street_name, a.street_type, a.street_quadrant, - a.house_number, a.house_alpha, a.longitude, a.latitude, a.visited_validated, - a.created_at, a.updated_at, - CASE WHEN ap.user_id IS NOT NULL THEN true ELSE false END as assigned, - COALESCE(u.first_name || ' ' || u.last_name, '') as user_name, - COALESCE(u.email, '') as user_email, - COALESCE(u.phone, '') as user_phone, - TO_CHAR(ap.appointment_date, 'YYYY-MM-DD') as appointment_date, - TO_CHAR(ap.appointment_time, 'HH24:MI') as appointment_time - FROM address_database a - LEFT JOIN appointment ap ON a.address_id = ap.address_id - LEFT JOIN users u ON ap.user_id = u.user_id - ORDER BY a.address_id; - `) - if err != nil { - log.Printf("query error: %v", err) - http.Error(w, "query error", http.StatusInternalServerError) - return - } - defer rows.Close() - - var assignedAddresses []AssignedAddress - for rows.Next() { - var addr AssignedAddress - err := rows.Scan( - &addr.AddressID, &addr.Address, &addr.StreetName, &addr.StreetType, &addr.StreetQuadrant, - &addr.HouseNumber, &addr.HouseAlpha, &addr.Longitude, &addr.Latitude, &addr.VisitedValidated, - &addr.CreatedAt, &addr.UpdatedAt, &addr.Assigned, &addr.UserName, &addr.UserEmail, - &addr.UserPhone, &addr.AppointmentDate, &addr.AppointmentTime, - ) - if err != nil { - log.Printf("scan error: %v", err) - continue - } - assignedAddresses = append(assignedAddresses, addr) - } - - utils.Render(w, "address_assigned.html", map[string]interface{}{ - "Title": "Assigned Addresses", - "IsAuthenticated": true, - "AssignedList": assignedAddresses, - "ShowAdminNav": true, - "Role": "admin", - "UserName": username, - "ActiveSection": "assigned", - }) -} diff --git a/app/internal/handlers/admin_voluteers.go b/app/internal/handlers/admin_voluteers.go index fd6038e..66930c2 100644 --- a/app/internal/handlers/admin_voluteers.go +++ b/app/internal/handlers/admin_voluteers.go @@ -51,6 +51,8 @@ func VolunteerHandler(w http.ResponseWriter, r *http.Request) { } func EditVolunteerHandler(w http.ResponseWriter, r *http.Request) { + username,_ := models.GetCurrentUserName(r) + if r.Method == http.MethodGet { volunteerID := r.URL.Query().Get("id") var user models.User @@ -69,6 +71,7 @@ func EditVolunteerHandler(w http.ResponseWriter, r *http.Request) { "Title": "Edit Volunteer", "IsAuthenticated": true, "ShowAdminNav": true, + "UserName": username, "Volunteer": user, "ActiveSection": "volunteer", }) diff --git a/app/internal/models/structs.go b/app/internal/models/structs.go index a4262e9..4e1e701 100644 --- a/app/internal/models/structs.go +++ b/app/internal/models/structs.go @@ -39,7 +39,7 @@ type User struct { Phone string Password string RoleID int - AdminCode string + AdminCode *string CreatedAt time.Time UpdatedAt time.Time } diff --git a/app/internal/templates/address/address.html b/app/internal/templates/address/address.html index d8a8441..51e2904 100644 --- a/app/internal/templates/address/address.html +++ b/app/internal/templates/address/address.html @@ -104,6 +104,7 @@ Assigned User Appointment Assign + Remove @@ -163,6 +164,26 @@ {{ end }} + + {{ if .Assigned }} +
+ + + +
+ {{ else }} + - + {{ end }} + {{ else }} diff --git a/app/internal/templates/layout.html b/app/internal/templates/layout.html index 7dca51c..b49864b 100644 --- a/app/internal/templates/layout.html +++ b/app/internal/templates/layout.html @@ -4,6 +4,8 @@ + + {{if .Title}}{{.Title}}{{else}}Poll System{{end}} @@ -42,8 +44,8 @@ + - -
-
-

Powerful Features

-

Everything you need to manage your polling operations efficiently and effectively.

+ +
+
+
+

Our Products

+

Tools built for modern polling operations

-
-
-
- -
-

Volunteer Management

-

Organize and coordinate your volunteer teams efficiently with role-based access and scheduling.

+ +
+
+ Poll Manager +

Poll Manager

+

Complete polling campaign management with real-time tracking and coordination tools.

+
    +
  • • Volunteer coordination
  • +
  • • Address management
  • +
  • • Progress tracking
  • +
-
-
- -
-

Address Tracking

-

Keep track of all polling locations and assignments with real-time updates and mapping.

+ +
+ Analytics Suite +

Analytics Suite

+

Advanced reporting and analytics dashboard for data-driven insights.

+
    +
  • • Performance metrics
  • +
  • • Custom reports
  • +
  • • Data visualization
  • +
-
-
- -
-

Real-time Reports

-

Monitor progress with comprehensive analytics and detailed reporting dashboards.

+ +
+ Team Builder +

Team Builder

+

Organize teams and assign roles with automated scheduling and notifications.

+
    +
  • • Role assignment
  • +
  • • Schedule management
  • +
  • • Team communication
  • +
-
+
+
- -
-
-
-
-

About Poll System

-

- Poll System was created to simplify and streamline the complex process of managing polling operations. - Our platform brings together volunteers, administrators, and team leaders in one unified system. -

-

- With years of experience in civic technology, we understand the challenges faced by polling organizations. - Our solution provides the tools needed to coordinate effectively and ensure smooth operations. -

-
-
-
- -
- Streamlined volunteer coordination -
-
-
- -
- Real-time progress tracking -
-
-
- -
- Comprehensive reporting tools -
+ +
+
+
+
+

About Linq

+

+ Built for organizations that need efficient polling management. + Our platform simplifies volunteer coordination and progress tracking. +

+ +
+
+
500+
+
Volunteers
-
-
-
-
- -

Trusted by Organizations

-

- Join hundreds of organizations already using Poll System to manage their operations efficiently. -

-
-
-
500+
-
Volunteers
-
-
-
50+
-
Organizations
-
-
-
1000+
-
Addresses
-
-
-
+ +
+
400,000+
+
Addresses
-
-
- - - -
+
+
+ + +