updated layout and volunteer
change the ui and now able to remove assigned addresses from volunteer
@@ -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,
|
||||
@@ -325,3 +328,65 @@ func AssignAddressHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// Redirect back to addresses page with success
|
||||
http.Redirect(w, r, "/addresses?success=assigned", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
})
|
||||
}
|
||||
@@ -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",
|
||||
})
|
||||
|
||||
@@ -39,7 +39,7 @@ type User struct {
|
||||
Phone string
|
||||
Password string
|
||||
RoleID int
|
||||
AdminCode string
|
||||
AdminCode *string
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
@@ -104,6 +104,7 @@
|
||||
<th class="px-6 py-3 whitespace-nowrap">Assigned User</th>
|
||||
<th class="px-6 py-3 whitespace-nowrap">Appointment</th>
|
||||
<th class="px-6 py-3 whitespace-nowrap">Assign</th>
|
||||
<th class="px-6 py-3 whitespace-nowrap">Remove</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200">
|
||||
@@ -163,6 +164,26 @@
|
||||
</button>
|
||||
{{ end }}
|
||||
</td>
|
||||
<td class="px-6 py-3 whitespace-nowrap">
|
||||
{{ if .Assigned }}
|
||||
<form
|
||||
action="/remove_assigned_address"
|
||||
method="POST"
|
||||
class="inline-block"
|
||||
>
|
||||
<input type="hidden" name="address_id" value="{{ .AddressID }}" />
|
||||
<input type="hidden" name="user_id" value="{{ .UserID }}" />
|
||||
<button
|
||||
type="submit"
|
||||
class="text-red-600 hover:text-red-800 font-medium text-xs px-2 py-1 hover:bg-red-50 transition-colors"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</form>
|
||||
{{ else }}
|
||||
<span class="text-gray-400 text-xs">-</span>
|
||||
{{ end }}
|
||||
</td>
|
||||
</tr>
|
||||
{{ else }}
|
||||
<tr>
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="icon" type="image/x-icon" href="../../static/favicon.ico">
|
||||
|
||||
<title>{{if .Title}}{{.Title}}{{else}}Poll System{{end}}</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script src="//unpkg.com/alpinejs" defer></script>
|
||||
@@ -42,8 +44,8 @@
|
||||
<div class="hidden lg:flex bg-gray-100 px-4 py-3 items-center justify-between border-b border-gray-200">
|
||||
<!-- Left Side: Logo + Title -->
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-6 h-6 bg-orange-500 rounded-full text-white text-xs flex items-center justify-center font-bold">
|
||||
L
|
||||
<div class="w-6 h-6 flex items-center justify-center">
|
||||
<img src="../../static/icon-512.png" alt="Logo" class="w-6 h-6"/>
|
||||
</div>
|
||||
<span class="text-sm font-medium">Poll System</span>
|
||||
</div>
|
||||
@@ -79,7 +81,7 @@
|
||||
x-transition:enter="transition ease-in-out duration-300 transform"
|
||||
x-transition:enter-start="-translate-x-full"
|
||||
x-transition:enter-end="translate-x-0"
|
||||
x-transition:leave="transition ease-in-out duration-300 transform"
|
||||
x-transition:leave="transition ease-in-out duration-300 transform"
|
||||
x-transition:leave-start="translate-x-0"
|
||||
x-transition:leave-end="-translate-x-full">
|
||||
|
||||
@@ -178,248 +180,213 @@
|
||||
</div>
|
||||
{{else}}
|
||||
<!-- Landing Page -->
|
||||
<div class="min-h-screen bg-gradient-to-br from-slate-50 to-gray-100" x-data="{ mobileMenuOpen: false }">
|
||||
<!-- Fixed Navigation -->
|
||||
<nav class="fixed top-0 w-full bg-white/90 backdrop-blur-md shadow-sm border-b border-gray-200 z-40">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex justify-between items-center h-16">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-8 h-8 bg-blue-600 text-white text-sm flex items-center justify-center font-bold">
|
||||
L
|
||||
</div>
|
||||
<span class="text-xl font-semibold text-gray-900">Poll System</span>
|
||||
</div>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Linq - Poll System</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
</head>
|
||||
<body class="bg-gray-50">
|
||||
<div class="min-h-screen" x-data="{ mobileMenuOpen: false }">
|
||||
|
||||
<!-- Desktop Navigation -->
|
||||
<div class="hidden md:flex items-center gap-6">
|
||||
<a href="#home" class="text-gray-600 hover:text-gray-900 font-medium transition-colors">Home</a>
|
||||
<a href="#features" class="text-gray-600 hover:text-gray-900 font-medium transition-colors">Features</a>
|
||||
<a href="#about" class="text-gray-600 hover:text-gray-900 font-medium transition-colors">About</a>
|
||||
</div>
|
||||
<!-- Navigation -->
|
||||
<nav class="bg-white border-b border-gray-200">
|
||||
<div class="max-w-7xl mx-auto px-6">
|
||||
<div class="flex justify-between items-center h-16">
|
||||
<!-- Logo -->
|
||||
<div class="flex items-center gap-3">
|
||||
<img src="../../static/icon-512.png" alt="Logo" class="w-8 h-8"/>
|
||||
<span class="text-xl font-semibold text-gray-900">Linq</span>
|
||||
</div>
|
||||
|
||||
<!-- Desktop Auth Buttons -->
|
||||
<div class="hidden md:flex items-center gap-3">
|
||||
<button onclick="openLoginModal()" class="px-4 py-2 text-gray-600 hover:text-gray-900 font-medium transition-colors">
|
||||
<!-- Desktop Navigation -->
|
||||
<div class="hidden md:flex items-center gap-8">
|
||||
<a href="#home" class="text-gray-600 hover:text-gray-900">Home</a>
|
||||
<a href="#products" class="text-gray-600 hover:text-gray-900">Products</a>
|
||||
<a href="#about" class="text-gray-600 hover:text-gray-900">About</a>
|
||||
</div>
|
||||
|
||||
<!-- Auth Buttons -->
|
||||
<div class="hidden md:flex items-center gap-3">
|
||||
<button onclick="openLoginModal()" class="px-4 py-2 text-gray-600 hover:text-gray-900">
|
||||
Sign In
|
||||
</button>
|
||||
<button onclick="openRegisterModal()" class="px-6 py-2 bg-blue-600 text-white hover:bg-blue-700 rounded">
|
||||
Get Started
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Menu Button -->
|
||||
<button @click="mobileMenuOpen = !mobileMenuOpen" class="md:hidden p-2">
|
||||
<i class="fas fa-bars text-gray-600" x-show="!mobileMenuOpen"></i>
|
||||
<i class="fas fa-times text-gray-600" x-show="mobileMenuOpen"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Menu -->
|
||||
<div x-show="mobileMenuOpen" class="md:hidden border-t border-gray-200 bg-white">
|
||||
<div class="px-6 py-4 space-y-3">
|
||||
<a href="#home" @click="mobileMenuOpen = false" class="block py-2 text-gray-600">Home</a>
|
||||
<a href="#products" @click="mobileMenuOpen = false" class="block py-2 text-gray-600">Products</a>
|
||||
<a href="#about" @click="mobileMenuOpen = false" class="block py-2 text-gray-600">About</a>
|
||||
<div class="border-t border-gray-200 pt-3 space-y-2">
|
||||
<button onclick="openLoginModal(); document.querySelector('[x-data]').__x.$data.mobileMenuOpen = false"
|
||||
class="block w-full text-left py-2 text-gray-600">
|
||||
Sign In
|
||||
</button>
|
||||
<button onclick="openRegisterModal()" class="px-4 py-2 bg-blue-600 text-white hover:bg-blue-700 font-medium transition-colors rounded">
|
||||
<button onclick="openRegisterModal(); document.querySelector('[x-data]').__x.$data.mobileMenuOpen = false"
|
||||
class="block w-full py-2 bg-blue-600 text-white rounded">
|
||||
Get Started
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Menu Button -->
|
||||
<div class="md:hidden">
|
||||
<button @click="mobileMenuOpen = !mobileMenuOpen" class="p-2 text-gray-600 hover:text-gray-900">
|
||||
<i class="fas fa-bars text-xl" x-show="!mobileMenuOpen"></i>
|
||||
<i class="fas fa-times text-xl" x-show="mobileMenuOpen"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Menu -->
|
||||
<div x-show="mobileMenuOpen"
|
||||
x-transition:enter="transition ease-out duration-200"
|
||||
x-transition:enter-start="opacity-0 scale-95"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-75"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-95"
|
||||
class="md:hidden bg-white border-t border-gray-200">
|
||||
<div class="px-4 py-4 space-y-3">
|
||||
<a href="#home" @click="mobileMenuOpen = false" class="block text-gray-600 hover:text-gray-900 font-medium py-2">Home</a>
|
||||
<a href="#features" @click="mobileMenuOpen = false" class="block text-gray-600 hover:text-gray-900 font-medium py-2">Features</a>
|
||||
<a href="#about" @click="mobileMenuOpen = false" class="block text-gray-600 hover:text-gray-900 font-medium py-2">About</a>
|
||||
<div class="border-t border-gray-200 pt-3 space-y-2">
|
||||
<button onclick="openLoginModal(); document.querySelector('[x-data]').__x.$data.mobileMenuOpen = false" class="block w-full text-left px-4 py-2 text-gray-600 hover:bg-gray-100 rounded font-medium">
|
||||
Sign In
|
||||
</button>
|
||||
<button onclick="openRegisterModal(); document.querySelector('[x-data]').__x.$data.mobileMenuOpen = false" class="block w-full px-4 py-2 bg-blue-600 text-white hover:bg-blue-700 font-medium rounded">
|
||||
Get Started
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Hero Section -->
|
||||
<section id="home" class="max-w-4xl mx-auto h-[600px] px-4 pt-[200px] pb-32 text-center">
|
||||
<h1 class="text-3xl sm:text-4xl lg:text-5xl font-bold text-gray-900 mb-6 leading-tight">
|
||||
Streamline Your<br>
|
||||
<span class="text-blue-600">Polling Operations</span>
|
||||
<!-- Hero -->
|
||||
<section id="home" class="pt-20 pb-16 px-6">
|
||||
<div class="max-w-4xl mx-auto text-center">
|
||||
<h1 class="text-4xl sm:text-5xl font-bold text-gray-900 mb-6">
|
||||
Simple Polling
|
||||
<span class="text-blue-600">Management</span>
|
||||
</h1>
|
||||
<p class="text-lg sm:text-xl text-gray-600 mb-8 max-w-2xl mx-auto leading-relaxed">
|
||||
Manage volunteers, organize addresses, and track progress with our comprehensive polling system.
|
||||
|
||||
<p class="text-xl text-gray-600 mb-8 max-w-2xl mx-auto">
|
||||
Manage volunteers and track polling operations efficiently.
|
||||
</p>
|
||||
|
||||
<div class="flex flex-col sm:flex-row justify-center gap-4">
|
||||
<button onclick="openRegisterModal()" class="px-8 py-3 bg-blue-600 text-white hover:bg-blue-700 font-semibold transition-colors rounded">
|
||||
Start Now
|
||||
<button onclick="openRegisterModal()" class="px-8 py-3 bg-blue-600 text-white hover:bg-blue-700 font-medium rounded">
|
||||
Start Free
|
||||
</button>
|
||||
<button onclick="openLoginModal()" class="px-8 py-3 border border-gray-300 text-gray-700 hover:bg-gray-50 font-semibold transition-colors rounded">
|
||||
<button onclick="openLoginModal()" class="px-8 py-3 border border-gray-300 text-gray-700 hover:bg-gray-50 font-medium rounded">
|
||||
Sign In
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Features Section -->
|
||||
<section id="features" class="max-w-6xl mx-auto px-4 py-20">
|
||||
<div class="text-center mb-16">
|
||||
<h2 class="text-3xl sm:text-4xl font-bold text-gray-900 mb-4">Powerful Features</h2>
|
||||
<p class="text-lg sm:text-xl text-gray-600 max-w-3xl mx-auto">Everything you need to manage your polling operations efficiently and effectively.</p>
|
||||
<!-- Products Section -->
|
||||
<section id="products" class="py-16 bg-white">
|
||||
<div class="max-w-6xl mx-auto px-6">
|
||||
<div class="text-center mb-12">
|
||||
<h2 class="text-3xl font-bold text-gray-900 mb-4">Our Products</h2>
|
||||
<p class="text-lg text-gray-600">Tools built for modern polling operations</p>
|
||||
</div>
|
||||
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
<div class="bg-white p-8 shadow-sm border border-gray-200 hover:shadow-md transition-shadow rounded-lg">
|
||||
<div class="w-12 h-12 bg-blue-100 rounded flex items-center justify-center mb-4">
|
||||
<i class="fas fa-users text-blue-600 text-xl"></i>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold mb-3">Volunteer Management</h3>
|
||||
<p class="text-gray-600">Organize and coordinate your volunteer teams efficiently with role-based access and scheduling.</p>
|
||||
|
||||
<div class="grid md:grid-cols-3 gap-8">
|
||||
<div class="p-6 border border-gray-200 rounded-lg">
|
||||
<img src="../../static/icon-512.png" alt="Poll Manager" class="w-12 h-12 mb-4"/>
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-3">Poll Manager</h3>
|
||||
<p class="text-gray-600 mb-4">Complete polling campaign management with real-time tracking and coordination tools.</p>
|
||||
<ul class="text-sm text-gray-500 space-y-1">
|
||||
<li>• Volunteer coordination</li>
|
||||
<li>• Address management</li>
|
||||
<li>• Progress tracking</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="bg-white p-8 shadow-sm border border-gray-200 hover:shadow-md transition-shadow rounded-lg">
|
||||
<div class="w-12 h-12 bg-green-100 rounded flex items-center justify-center mb-4">
|
||||
<i class="fas fa-map-marker-alt text-green-600 text-xl"></i>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold mb-3">Address Tracking</h3>
|
||||
<p class="text-gray-600">Keep track of all polling locations and assignments with real-time updates and mapping.</p>
|
||||
|
||||
<div class="p-6 border border-gray-200 rounded-lg">
|
||||
<img src="../../static/icon-512.png" alt="Analytics Suite" class="w-12 h-12 mb-4"/>
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-3">Analytics Suite</h3>
|
||||
<p class="text-gray-600 mb-4">Advanced reporting and analytics dashboard for data-driven insights.</p>
|
||||
<ul class="text-sm text-gray-500 space-y-1">
|
||||
<li>• Performance metrics</li>
|
||||
<li>• Custom reports</li>
|
||||
<li>• Data visualization</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="bg-white p-8 shadow-sm border border-gray-200 hover:shadow-md transition-shadow rounded-lg sm:col-span-2 lg:col-span-1">
|
||||
<div class="w-12 h-12 bg-purple-100 rounded flex items-center justify-center mb-4">
|
||||
<i class="fas fa-chart-bar text-purple-600 text-xl"></i>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold mb-3">Real-time Reports</h3>
|
||||
<p class="text-gray-600">Monitor progress with comprehensive analytics and detailed reporting dashboards.</p>
|
||||
|
||||
<div class="p-6 border border-gray-200 rounded-lg">
|
||||
<img src="../../static/icon-512.png" alt="Team Builder" class="w-12 h-12 mb-4"/>
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-3">Team Builder</h3>
|
||||
<p class="text-gray-600 mb-4">Organize teams and assign roles with automated scheduling and notifications.</p>
|
||||
<ul class="text-sm text-gray-500 space-y-1">
|
||||
<li>• Role assignment</li>
|
||||
<li>• Schedule management</li>
|
||||
<li>• Team communication</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- About Section -->
|
||||
<section id="about" class="bg-white py-20">
|
||||
<div class="max-w-6xl mx-auto px-4">
|
||||
<div class="grid lg:grid-cols-2 gap-12 items-center">
|
||||
<div>
|
||||
<h2 class="text-3xl sm:text-4xl font-bold text-gray-900 mb-6">About Poll System</h2>
|
||||
<p class="text-base sm:text-lg text-gray-600 mb-6">
|
||||
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.
|
||||
</p>
|
||||
<p class="text-base sm:text-lg text-gray-600 mb-8">
|
||||
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.
|
||||
</p>
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-6 h-6 bg-green-100 rounded flex items-center justify-center flex-shrink-0">
|
||||
<i class="fas fa-check text-green-600 text-sm"></i>
|
||||
</div>
|
||||
<span class="text-gray-700">Streamlined volunteer coordination</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-6 h-6 bg-green-100 rounded flex items-center justify-center flex-shrink-0">
|
||||
<i class="fas fa-check text-green-600 text-sm"></i>
|
||||
</div>
|
||||
<span class="text-gray-700">Real-time progress tracking</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-6 h-6 bg-green-100 rounded flex items-center justify-center flex-shrink-0">
|
||||
<i class="fas fa-check text-green-600 text-sm"></i>
|
||||
</div>
|
||||
<span class="text-gray-700">Comprehensive reporting tools</span>
|
||||
</div>
|
||||
<!-- About -->
|
||||
<section id="about" class="py-16 bg-gray-50">
|
||||
<div class="max-w-6xl mx-auto px-6">
|
||||
<div class="grid lg:grid-cols-2 gap-12 items-center">
|
||||
<div>
|
||||
<h2 class="text-3xl font-bold text-gray-900 mb-6">About Linq</h2>
|
||||
<p class="text-lg text-gray-600 mb-6">
|
||||
Built for organizations that need efficient polling management.
|
||||
Our platform simplifies volunteer coordination and progress tracking.
|
||||
</p>
|
||||
|
||||
<div class="grid grid-cols-3 gap-6 mb-8">
|
||||
<div class="text-center">
|
||||
<div class="text-2xl font-bold text-gray-900">500+</div>
|
||||
<div class="text-sm text-gray-500">Volunteers</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative">
|
||||
<div class="bg-gradient-to-br from-blue-500 to-blue-700 p-8 text-white rounded-lg">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-users text-6xl mb-6 opacity-20"></i>
|
||||
<h3 class="text-2xl font-bold mb-4">Trusted by Organizations</h3>
|
||||
<p class="text-lg opacity-90 mb-6">
|
||||
Join hundreds of organizations already using Poll System to manage their operations efficiently.
|
||||
</p>
|
||||
<div class="grid grid-cols-3 gap-4 text-center">
|
||||
<div>
|
||||
<div class="text-2xl font-bold">500+</div>
|
||||
<div class="text-sm opacity-80">Volunteers</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-2xl font-bold">50+</div>
|
||||
<div class="text-sm opacity-80">Organizations</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-2xl font-bold">1000+</div>
|
||||
<div class="text-sm opacity-80">Addresses</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="text-center">
|
||||
<div class="text-2xl font-bold text-gray-900">50+</div>
|
||||
<div class="text-sm text-gray-500">Organizations</div>
|
||||
</div> -->
|
||||
<div class="text-center">
|
||||
<div class="text-2xl font-bold text-gray-900">400,000+</div>
|
||||
<div class="text-sm text-gray-500">Addresses</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="bg-gray-900 text-white py-12">
|
||||
<div class="max-w-6xl mx-auto px-4">
|
||||
<div class="grid sm:grid-cols-2 lg:grid-cols-4 gap-8">
|
||||
<div class="sm:col-span-2 lg:col-span-2">
|
||||
<div class="flex items-center gap-2 mb-4">
|
||||
<div class="w-8 h-8 bg-blue-600 text-white text-sm flex items-center justify-center font-bold rounded">
|
||||
L
|
||||
</div>
|
||||
<span class="text-xl font-semibold">Poll System</span>
|
||||
</div>
|
||||
<p class="text-gray-400 mb-4 max-w-md">
|
||||
Streamlining polling operations with comprehensive volunteer management,
|
||||
address tracking, and real-time reporting capabilities.
|
||||
</p>
|
||||
<div class="flex gap-4">
|
||||
<a href="#" class="w-10 h-10 bg-gray-800 rounded flex items-center justify-center hover:bg-blue-600 transition-colors">
|
||||
<i class="fab fa-twitter"></i>
|
||||
</a>
|
||||
<a href="#" class="w-10 h-10 bg-gray-800 rounded flex items-center justify-center hover:bg-blue-600 transition-colors">
|
||||
<i class="fab fa-linkedin"></i>
|
||||
</a>
|
||||
<a href="#" class="w-10 h-10 bg-gray-800 rounded flex items-center justify-center hover:bg-blue-600 transition-colors">
|
||||
<i class="fab fa-github"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="font-semibold mb-4">Platform</h4>
|
||||
<ul class="space-y-2 text-gray-400">
|
||||
<li><a href="#" class="hover:text-white transition-colors">Dashboard</a></li>
|
||||
<li><a href="#" class="hover:text-white transition-colors">Volunteers</a></li>
|
||||
<li><a href="#" class="hover:text-white transition-colors">Addresses</a></li>
|
||||
<li><a href="#" class="hover:text-white transition-colors">Reports</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="font-semibold mb-4">Support</h4>
|
||||
<ul class="space-y-2 text-gray-400">
|
||||
<li><a href="#" class="hover:text-white transition-colors">Help Center</a></li>
|
||||
<li><a href="#" class="hover:text-white transition-colors">Contact Us</a></li>
|
||||
<li><a href="#" class="hover:text-white transition-colors">Privacy Policy</a></li>
|
||||
<li><a href="#" class="hover:text-white transition-colors">Terms of Service</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border-t border-gray-800 mt-8 pt-8 text-center text-gray-400">
|
||||
<p>© 2025 Poll System. All rights reserved.</p>
|
||||
<div class="bg-white p-8 rounded-lg border border-gray-200">
|
||||
<img src="../../static/feature-mobile4.jpg" alt="Dashboard Preview" class="w-full h-48 object-cover rounded mb-6 bg-gray-100"/>
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-3">Simple Dashboard</h3>
|
||||
<p class="text-gray-600">
|
||||
Everything you need in one place. Track progress, manage teams, and generate reports.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="bg-white border-t border-gray-200 py-8">
|
||||
<div class="max-w-6xl mx-auto px-6">
|
||||
<div class="flex flex-col md:flex-row justify-between items-center gap-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<img src="../../static/icon-512.png" alt="Logo" class="w-6 h-6"/>
|
||||
<span class="font-semibold text-gray-900">Linq</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-6 text-sm text-gray-500">
|
||||
<a href="#" class="hover:text-gray-900">Privacy</a>
|
||||
<a href="#" class="hover:text-gray-900">Terms</a>
|
||||
<a href="#" class="hover:text-gray-900">Contact</a>
|
||||
</div>
|
||||
|
||||
<p class="text-sm text-gray-500">© 2025 Linq. All rights reserved.</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
<!-- Login Modal -->
|
||||
<div id="loginModal" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center z-50 p-4">
|
||||
<div class="bg-white shadow-2xl max-w-4xl w-full overflow-hidden rounded-lg">
|
||||
<div class="flex flex-col lg:flex-row min-h-[500px]">
|
||||
<!-- Left Side - Image -->
|
||||
<div class="flex-1 bg-gradient-to-br from-blue-500 to-blue-700 flex items-center justify-center p-8">
|
||||
<div class="text-center text-white">
|
||||
<div class="hidden lg:flex flex-1 bg-gradient-to-br from-blue-500 to-blue-700 flex-col items-center justify-center">
|
||||
<!-- <div class="text-center text-white">
|
||||
<i class="fas fa-chart-line text-4xl sm:text-6xl mb-6"></i>
|
||||
<h2 class="text-2xl sm:text-3xl font-bold mb-4">Welcome Back</h2>
|
||||
<p class="text-base sm:text-lg opacity-90">Continue managing your polling operations</p>
|
||||
</div>
|
||||
</div> -->
|
||||
<img src="../../static/feature-mobile2.jpg" alt="Welcome Image" class="object-cover h-full rounded-lg shadow-lg">
|
||||
|
||||
</div>
|
||||
<!-- Right Side - Form -->
|
||||
<div class="flex-1 p-6 sm:p-8">
|
||||
@@ -458,13 +425,15 @@
|
||||
<div class="bg-white shadow-2xl max-w-4xl w-full overflow-hidden rounded-lg">
|
||||
<div class="flex flex-col lg:flex-row min-h-[600px]">
|
||||
<!-- Left Side - Image -->
|
||||
<div class="flex-1 bg-gradient-to-br from-blue-600 to-blue-800 flex items-center justify-center p-8">
|
||||
<div class="text-center text-white">
|
||||
<div class="hidden lg:flex flex-1 bg-gradient-to-br from-blue-600 to-blue-800 flex-col items-center justify-center">
|
||||
<!-- <div class="text-center text-white">
|
||||
<i class="fas fa-rocket text-4xl sm:text-6xl mb-6"></i>
|
||||
<h2 class="text-2xl sm:text-3xl font-bold mb-4">Get Started</h2>
|
||||
<p class="text-base sm:text-lg opacity-90">Join our platform and streamline your operations</p>
|
||||
</div>
|
||||
</div> -->
|
||||
<img src="../../static/feature-mobile1.jpg" alt="Welcome Image" class="object-cover h-full rounded-lg shadow-lg">
|
||||
</div>
|
||||
|
||||
<!-- Right Side - Form -->
|
||||
<div class="flex-1 p-6 sm:p-8 overflow-y-auto">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
@@ -533,7 +502,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<script>
|
||||
|
||||
@@ -134,34 +134,9 @@
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<!-- Post Actions -->
|
||||
<div class="px-6 py-3">
|
||||
<div class="flex items-center space-x-6">
|
||||
<!-- Like Button -->
|
||||
<button
|
||||
class="reaction-btn flex items-center space-x-2 text-gray-600 hover:text-blue-500 transition-colors"
|
||||
data-post-id="{{.PostID}}"
|
||||
data-reaction="like"
|
||||
>
|
||||
<i class="fa-solid fa-thumbs-up text-lg"></i>
|
||||
<span class="text-sm font-medium like-count">0</span>
|
||||
</button>
|
||||
|
||||
<!-- Dislike Button -->
|
||||
<button
|
||||
class="reaction-btn flex items-center space-x-2 text-gray-600 hover:text-red-500 transition-colors"
|
||||
data-post-id="{{.PostID}}"
|
||||
data-reaction="dislike"
|
||||
>
|
||||
<i class="fa-solid fa-thumbs-down text-lg"></i>
|
||||
<span class="text-sm font-medium dislike-count">0</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Post Content -->
|
||||
{{if .Content}}
|
||||
<div class="px-6 pb-4">
|
||||
<div class="px-6 pt-2 pb-4">
|
||||
<p class="text-gray-900 leading-relaxed">
|
||||
<span class="font-semibold">{{.AuthorName}}</span> {{.Content}}
|
||||
</p>
|
||||
|
||||
@@ -24,21 +24,26 @@
|
||||
</h3>
|
||||
<p class="text-gray-600">{{ .User.Email }}</p>
|
||||
<div class="flex items-center mt-2 space-x-4">
|
||||
<span class="text-gray-600">User Role:</span>
|
||||
<span
|
||||
class="inline-flex items-center px-2 py-1 text-xs font-medium bg-blue-100 text-blue-800 border border-blue-200"
|
||||
>
|
||||
<i class="fas fa-user-check mr-1"></i>
|
||||
Active User
|
||||
</span>
|
||||
<span class="text-gray-600">Signup Code:</span>
|
||||
<span class="font-mono text-gray-900">{{ .User.AdminCode }}</span>
|
||||
<span class="text-gray-600">User ID:</span>
|
||||
<span class="font-mono text-gray-900">{{ .User.UserID }}</span>
|
||||
<span class="text-gray-600">Role:</span>
|
||||
<span class="text-gray-900">
|
||||
{{ if eq .User.RoleID 1 }}Admin {{ else if eq .User.RoleID 2
|
||||
}}Team Leader {{ else }}Volunteer {{ end }}
|
||||
</span>
|
||||
<span class="text-gray-600">User ID:</span>
|
||||
<span
|
||||
class="inline-flex items-center px-2 py-1 text-xs font-medium bg-blue-100 text-blue-800 border border-blue-200"
|
||||
>
|
||||
<i class="fas fa-user-check mr-1"></i>
|
||||
|
||||
{{ .User.UserID }}</span
|
||||
>
|
||||
{{if eq .User.RoleID 1}}
|
||||
<span class="text-gray-600">Signup Code:</span>
|
||||
<span class="font-mono text-gray-900">{{.User.AdminCode }} </span>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -152,99 +157,6 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Configuration Settings Section -->
|
||||
<div class="border-t border-gray-200 pt-8 mt-8">
|
||||
<h3 class="text-lg font-semibold text-gray-900 mb-6 flex items-center">
|
||||
<i class="fas fa-cog text-blue-600 mr-3"></i>
|
||||
Configuration Settings
|
||||
</h3>
|
||||
|
||||
<form method="post" action="/profile/settings" id="settingsForm">
|
||||
<div class="space-y-6">
|
||||
<!-- Add New Setting -->
|
||||
<div class="bg-white border border-gray-200 p-6">
|
||||
<h4 class="text-md font-semibold text-gray-800 mb-4">
|
||||
Add New Setting
|
||||
</h4>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-2">
|
||||
Setting Name
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="newSettingName"
|
||||
class="w-full px-4 py-3 border border-gray-300 focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500 bg-white"
|
||||
placeholder="Enter setting name"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-2">
|
||||
Setting Value
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="newSettingValue"
|
||||
class="w-full px-4 py-3 border border-gray-300 focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500 bg-white"
|
||||
placeholder="Enter setting value"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-end">
|
||||
<button
|
||||
type="button"
|
||||
onclick="addSetting()"
|
||||
class="w-full px-6 py-3 bg-green-600 text-white hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-green-500 font-medium"
|
||||
>
|
||||
<i class="fas fa-plus mr-2"></i>
|
||||
Add Setting
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Current Settings -->
|
||||
<div class="bg-white border border-gray-200 p-6">
|
||||
<h4 class="text-md font-semibold text-gray-800 mb-4">
|
||||
Current Settings
|
||||
</h4>
|
||||
<div id="settingsList" class="space-y-3">
|
||||
<!-- Settings will be dynamically added here -->
|
||||
<div class="text-gray-500 text-sm" id="noSettingsMessage">
|
||||
No settings configured yet. Add your first setting above.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Settings Form Actions -->
|
||||
<div
|
||||
class="mt-8 pt-6 border-t border-gray-200 flex justify-between items-center"
|
||||
>
|
||||
<div class="flex items-center text-sm text-gray-500">
|
||||
<i class="fas fa-info-circle text-blue-500 mr-2"></i>
|
||||
Settings are applied immediately when added or removed
|
||||
</div>
|
||||
<div class="flex space-x-3">
|
||||
<button
|
||||
type="button"
|
||||
onclick="clearAllSettings()"
|
||||
class="px-6 py-2 border border-red-300 text-red-700 bg-white hover:bg-red-50 focus:outline-none focus:ring-2 focus:ring-red-500 font-medium"
|
||||
>
|
||||
<i class="fas fa-trash mr-2"></i>
|
||||
Clear All
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
class="px-6 py-2 bg-blue-600 text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 font-medium"
|
||||
>
|
||||
<i class="fas fa-save mr-2"></i>
|
||||
Save Settings
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -27,19 +27,22 @@
|
||||
</h3>
|
||||
<p class="text-gray-600">{{ .Volunteer.Email }}</p>
|
||||
<div class="flex items-center mt-2 space-x-4">
|
||||
<span class="text-gray-600">User Role:</span>
|
||||
<span
|
||||
class="inline-flex items-center px-2 py-1 text-xs font-medium bg-blue-100 text-blue-800 border border-blue-200"
|
||||
>
|
||||
<i class="fas fa-user-check mr-1"></i>
|
||||
Volunteer
|
||||
</span>
|
||||
<span class="text-gray-600">User ID:</span>
|
||||
<span class="font-mono text-gray-900">{{ .Volunteer.UserID }}</span>
|
||||
<span class="text-gray-600">Current Role:</span>
|
||||
<span class="text-gray-900">
|
||||
{{ if eq .Volunteer.RoleID 2 }}Team Leader{{ else }}Volunteer{{
|
||||
end }}
|
||||
</span>
|
||||
<span class="text-gray-600">User ID:</span>
|
||||
<span
|
||||
class="inline-flex items-center px-2 py-1 text-xs font-medium bg-blue-100 text-blue-800 border border-blue-200"
|
||||
>
|
||||
<i class="fas fa-user-check mr-1"></i>
|
||||
|
||||
{{ .Volunteer.UserID }}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -107,6 +110,7 @@
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-2">
|
||||
Phone Number
|
||||
</label>
|
||||
r
|
||||
<input
|
||||
type="text"
|
||||
name="phone"
|
||||
@@ -127,13 +131,13 @@
|
||||
required
|
||||
class="w-full px-4 py-3 border border-gray-300 focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500 bg-white"
|
||||
>
|
||||
<option value="">--Select Role--</option>
|
||||
<option value="3" {{if eq .Volunteer.RoleID 3}}selected{{end}}>
|
||||
Volunteer
|
||||
</option>
|
||||
<option value="" disabled selected hidden>Select Role</option>
|
||||
<option value="2" {{if eq .Volunteer.RoleID 2}}selected{{end}}>
|
||||
Team Leader
|
||||
</option>
|
||||
<option value="3" {{if eq .Volunteer.RoleID 3}}selected{{end}}>
|
||||
Volunteer
|
||||
</option>
|
||||
</select>
|
||||
<p class="text-xs text-gray-500 mt-1">
|
||||
Team Leaders can manage volunteers and access additional features
|
||||
@@ -169,107 +173,6 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Configuration Settings Section -->
|
||||
<div class="border-t border-gray-200 pt-8 mt-8">
|
||||
<h3 class="text-lg font-semibold text-gray-900 mb-6 flex items-center">
|
||||
<i class="fas fa-cog text-blue-600 mr-3"></i>
|
||||
Volunteer Settings
|
||||
</h3>
|
||||
|
||||
<form
|
||||
method="post"
|
||||
action="/volunteer/settings"
|
||||
id="volunteerSettingsForm"
|
||||
>
|
||||
<div class="space-y-6">
|
||||
<!-- Add New Setting -->
|
||||
<div class="bg-white border border-gray-200 p-6">
|
||||
<h4 class="text-md font-semibold text-gray-800 mb-4">
|
||||
Add New Setting
|
||||
</h4>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-2">
|
||||
Setting Name
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="newVolunteerSettingName"
|
||||
class="w-full px-4 py-3 border border-gray-300 focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500 bg-white"
|
||||
placeholder="Enter setting name"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-2">
|
||||
Setting Value
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="newVolunteerSettingValue"
|
||||
class="w-full px-4 py-3 border border-gray-300 focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500 bg-white"
|
||||
placeholder="Enter setting value"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-end">
|
||||
<button
|
||||
type="button"
|
||||
onclick="addVolunteerSetting()"
|
||||
class="w-full px-6 py-3 bg-green-600 text-white hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-green-500 font-medium"
|
||||
>
|
||||
<i class="fas fa-plus mr-2"></i>
|
||||
Add Setting
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Current Settings -->
|
||||
<div class="bg-white border border-gray-200 p-6">
|
||||
<h4 class="text-md font-semibold text-gray-800 mb-4">
|
||||
Current Settings
|
||||
</h4>
|
||||
<div id="volunteerSettingsList" class="space-y-3">
|
||||
<!-- Settings will be dynamically added here -->
|
||||
<div
|
||||
class="text-gray-500 text-sm"
|
||||
id="noVolunteerSettingsMessage"
|
||||
>
|
||||
No settings configured for this volunteer yet. Add settings
|
||||
above.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Settings Form Actions -->
|
||||
<div
|
||||
class="mt-8 pt-6 border-t border-gray-200 flex justify-between items-center"
|
||||
>
|
||||
<div class="flex items-center text-sm text-gray-500">
|
||||
<i class="fas fa-info-circle text-blue-500 mr-2"></i>
|
||||
Settings are specific to this volunteer and applied immediately
|
||||
</div>
|
||||
<div class="flex space-x-3">
|
||||
<button
|
||||
type="button"
|
||||
onclick="clearAllVolunteerSettings()"
|
||||
class="px-6 py-2 border border-red-300 text-red-700 bg-white hover:bg-red-50 focus:outline-none focus:ring-2 focus:ring-red-500 font-medium"
|
||||
>
|
||||
<i class="fas fa-trash mr-2"></i>
|
||||
Clear All
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
class="px-6 py-2 bg-blue-600 text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 font-medium"
|
||||
>
|
||||
<i class="fas fa-save mr-2"></i>
|
||||
Save Settings
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -185,6 +185,8 @@ func main() {
|
||||
|
||||
http.HandleFunc("/addresses", adminMiddleware(handlers.AddressHandler))
|
||||
http.HandleFunc("/assign_address", adminMiddleware(handlers.AssignAddressHandler))
|
||||
http.HandleFunc("/remove_assigned_address", adminMiddleware(handlers.RemoveAssignedAddressHandler))
|
||||
|
||||
|
||||
|
||||
http.HandleFunc("/posts", adminMiddleware(handlers.PostsHandler))
|
||||
|
||||
BIN
app/static/favicon.ico
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
app/static/feature-mobile1.jpg
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
app/static/feature-mobile2.jpg
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
app/static/feature-mobile3.jpg
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
app/static/feature-mobile4.jpg
Normal file
|
After Width: | Height: | Size: 641 KiB |
BIN
app/static/icon-512.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
@@ -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 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
|
||||
BIN
app/tmp/main
BIN
app/uploads/3_1756358325869790000.jpg
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
app/uploads/3_1756358508202969000.jpg
Normal file
|
After Width: | Height: | Size: 43 KiB |