Files
Poll-system/app/internal/handlers/admin_addresses.go

327 lines
7.8 KiB
Go
Raw Normal View History

2025-08-26 14:13:09 -06:00
package handlers
import (
"log"
"net/http"
"strconv"
"github.com/patel-mann/poll-system/app/internal/models"
"github.com/patel-mann/poll-system/app/internal/utils"
)
// PaginationInfo holds pagination metadata
type PaginationInfo struct {
CurrentPage int
TotalPages int
TotalRecords int
PageSize int
HasPrevious bool
HasNext bool
StartRecord int
EndRecord int
PreviousPage int
NextPage int
FirstPage int
LastPage int
PageNumbers []PageNumber
}
type PageNumber struct {
Number int
IsCurrent bool
}
2025-08-27 13:21:11 -06:00
// AddressWithDetails extends AddressDatabase with appointment and user info
type AddressWithDetails struct {
models.AddressDatabase
UserName string
UserEmail string
AppointmentDate string
AppointmentTime string
}
2025-08-26 14:13:09 -06:00
func AddressHandler(w http.ResponseWriter, r *http.Request) {
// Get pagination parameters from query string
pageStr := r.URL.Query().Get("page")
pageSizeStr := r.URL.Query().Get("pageSize")
2025-08-27 13:21:11 -06:00
username,_ := models.GetCurrentUserName(r)
2025-08-26 14:13:09 -06:00
2025-08-27 13:21:11 -06:00
page := 1
pageSize := 20
2025-08-26 14:13:09 -06:00
if pageStr != "" {
if p, err := strconv.Atoi(pageStr); err == nil && p > 0 {
page = p
}
}
if pageSizeStr != "" {
if ps, err := strconv.Atoi(pageSizeStr); err == nil && ps > 0 && ps <= 100 {
pageSize = ps
}
}
offset := (page - 1) * pageSize
2025-08-27 13:21:11 -06:00
// Get total count
2025-08-26 14:13:09 -06:00
var totalRecords int
err := models.DB.QueryRow(`SELECT COUNT(*) FROM "address_database"`).Scan(&totalRecords)
if err != nil {
log.Println("Count query error:", err)
http.Error(w, "Database error", http.StatusInternalServerError)
return
}
totalPages := (totalRecords + pageSize - 1) / pageSize
if totalPages == 0 {
totalPages = 1
}
if page > totalPages {
page = totalPages
offset = (page - 1) * pageSize
}
2025-08-27 13:21:11 -06:00
// Query addresses with appointment + user info
2025-08-26 14:13:09 -06:00
rows, err := models.DB.Query(`
2025-08-27 13:21:11 -06:00
SELECT
a.address_id,
a.address,
a.street_name,
a.street_type,
a.street_quadrant,
a.house_number,
COALESCE(a.house_alpha, '') as house_alpha,
a.longitude,
a.latitude,
a.visited_validated,
a.created_at,
a.updated_at,
CASE
WHEN ap.sched_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(ap.appointment_date::text, '') as appointment_date,
COALESCE(ap.appointment_time::text, '') 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
WHERE a.street_quadrant = 'ne'
ORDER BY a.address_id
2025-08-26 14:13:09 -06:00
LIMIT $1 OFFSET $2
`, pageSize, offset)
if err != nil {
log.Println("Query error:", err)
http.Error(w, "Database error", http.StatusInternalServerError)
return
}
defer rows.Close()
2025-08-27 13:21:11 -06:00
var addresses []AddressWithDetails
2025-08-26 14:13:09 -06:00
for rows.Next() {
2025-08-27 13:21:11 -06:00
var a AddressWithDetails
var houseAlpha string
2025-08-26 14:13:09 -06:00
err := rows.Scan(
&a.AddressID,
&a.Address,
&a.StreetName,
&a.StreetType,
&a.StreetQuadrant,
&a.HouseNumber,
2025-08-27 13:21:11 -06:00
&houseAlpha,
2025-08-26 14:13:09 -06:00
&a.Longitude,
&a.Latitude,
&a.VisitedValidated,
2025-08-27 13:21:11 -06:00
&a.CreatedAt,
&a.UpdatedAt,
&a.Assigned,
&a.UserName,
&a.UserEmail,
&a.AppointmentDate,
&a.AppointmentTime,
2025-08-26 14:13:09 -06:00
)
if err != nil {
log.Println("Scan error:", err)
continue
}
2025-08-27 13:21:11 -06:00
// Handle nullable house_alpha
if houseAlpha != "" {
a.HouseAlpha = &houseAlpha
}
2025-08-26 14:13:09 -06:00
addresses = append(addresses, a)
}
2025-08-27 13:21:11 -06:00
// Get users associated with this admin
currentAdminID := r.Context().Value("user_id").(int)
userRows, err := models.DB.Query(`
SELECT u.user_id, u.first_name || ' ' || u.last_name AS name
FROM users u
JOIN admin_volunteers av ON u.user_id = av.volunteer_id
WHERE av.admin_id = $1 AND av.is_active = true
`, currentAdminID)
if err != nil {
log.Println("Failed to fetch users:", err)
http.Error(w, "Database error", http.StatusInternalServerError)
return
}
defer userRows.Close()
type UserOption struct {
ID int
Name string
}
var users []UserOption
for userRows.Next() {
var u UserOption
if err := userRows.Scan(&u.ID, &u.Name); err != nil {
log.Println("User scan error:", err)
continue
}
users = append(users, u)
}
// Pagination info
2025-08-26 14:13:09 -06:00
startRecord := offset + 1
endRecord := offset + len(addresses)
if totalRecords == 0 {
startRecord = 0
}
pageNumbers := generatePageNumbers(page, totalPages)
pagination := PaginationInfo{
CurrentPage: page,
TotalPages: totalPages,
TotalRecords: totalRecords,
PageSize: pageSize,
HasPrevious: page > 1,
HasNext: page < totalPages,
StartRecord: startRecord,
EndRecord: endRecord,
PreviousPage: page - 1,
NextPage: page + 1,
FirstPage: 1,
LastPage: totalPages,
PageNumbers: pageNumbers,
}
utils.Render(w, "address/address.html", map[string]interface{}{
"Title": "Addresses",
"IsAuthenticated": true,
"ShowAdminNav": true,
2025-08-27 13:21:11 -06:00
"ActiveSection": "address",
2025-08-26 14:13:09 -06:00
"Addresses": addresses,
2025-08-27 13:21:11 -06:00
"Users": users,
"UserName": username,
"Role": "admin",
2025-08-26 14:13:09 -06:00
"Pagination": pagination,
})
}
func generatePageNumbers(currentPage, totalPages int) []PageNumber {
var pageNumbers []PageNumber
// Generate page numbers to show (max 7 pages)
start := currentPage - 3
end := currentPage + 3
if start < 1 {
end += 1 - start
start = 1
}
if end > totalPages {
start -= end - totalPages
end = totalPages
}
if start < 1 {
start = 1
}
for i := start; i <= end; i++ {
pageNumbers = append(pageNumbers, PageNumber{
Number: i,
IsCurrent: i == currentPage,
})
}
return pageNumbers
}
2025-08-27 13:21:11 -06:00
func AssignAddressHandler(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 exists and is associated with the current admin
currentAdminID := r.Context().Value("user_id").(int)
var userExists int
err = models.DB.QueryRow(`
SELECT COUNT(*) FROM admin_volunteers av
JOIN users u ON av.volunteer_id = u.user_id
WHERE av.admin_id = $1 AND u.user_id = $2 AND av.is_active = true
`, currentAdminID, userID).Scan(&userExists)
if err != nil {
log.Println("User verification error:", err)
http.Error(w, "Database error", http.StatusInternalServerError)
return
}
if userExists == 0 {
http.Error(w, "Invalid user selection", http.StatusBadRequest)
return
}
// Check if this address is already assigned to any user
var exists int
err = models.DB.QueryRow(`
SELECT COUNT(*) FROM appointment
WHERE address_id = $1
`, addressID).Scan(&exists)
if err != nil {
log.Println("Assignment check error:", err)
http.Error(w, "Database error", http.StatusInternalServerError)
return
}
if exists > 0 {
http.Error(w, "This address is already assigned to another user", http.StatusBadRequest)
return
}
// Assign the address - create appointment
_, 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)
if err != nil {
log.Println("Assignment error:", err)
http.Error(w, "Failed to assign address", http.StatusInternalServerError)
return
}
// Redirect back to addresses page with success
http.Redirect(w, r, "/addresses?success=assigned", http.StatusSeeOther)
}