top bar update
This commit is contained in:
8
.gitignore
vendored
8
.gitignore
vendored
@@ -1,4 +1,4 @@
|
||||
/uploads
|
||||
.env
|
||||
/tmp
|
||||
/Example_code
|
||||
/app/uploads
|
||||
/app/.env
|
||||
/app/tmp/
|
||||
/app/Example_code
|
||||
|
||||
402
Example_code/admin.go
Normal file
402
Example_code/admin.go
Normal file
@@ -0,0 +1,402 @@
|
||||
// package handlers
|
||||
|
||||
// import (
|
||||
// "database/sql"
|
||||
// "errors"
|
||||
// "log"
|
||||
// "net/http"
|
||||
// "strconv"
|
||||
// "time"
|
||||
|
||||
// "github.com/patel-mann/poll-system/app/internal/models"
|
||||
// "github.com/patel-mann/poll-system/app/internal/utils"
|
||||
// )
|
||||
|
||||
// // View model for listing/assigning schedules
|
||||
// type AssignmentVM struct {
|
||||
// ID int
|
||||
// VolunteerID int
|
||||
// VolunteerName string
|
||||
// AddressID int
|
||||
// Address string
|
||||
// Date string // YYYY-MM-DD (for input[type=date])
|
||||
// AppointmentTime string // HH:MM
|
||||
// VisitedValidated bool
|
||||
// }
|
||||
|
||||
// // GET + POST in one handler:
|
||||
// // - GET: show assignments + form to assign
|
||||
// // - POST: create a new assignment
|
||||
// func AdminAssignmentsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// switch r.Method {
|
||||
// case http.MethodPost:
|
||||
// if err := createAssignmentFromForm(r); err != nil {
|
||||
// log.Println("create assignment error:", err)
|
||||
// volunteers, _ := fetchVolunteers()
|
||||
// addresses, _ := fetchAddresses()
|
||||
// assignments, _ := fetchAssignments()
|
||||
|
||||
// utils.Render(w, "schedual/assignments.html", map[string]interface{}{
|
||||
// "Title": "Admin — Assign Addresses",
|
||||
// "IsAuthenticated": true,
|
||||
// "ActiveSection": "admin_assignments",
|
||||
// "Volunteers": volunteers,
|
||||
// "Addresses": addresses,
|
||||
// "Assignments": assignments,
|
||||
// "Error": err.Error(),
|
||||
// })
|
||||
// return
|
||||
// }
|
||||
// http.Redirect(w, r, "/admin/assignments", http.StatusSeeOther)
|
||||
// return
|
||||
// }
|
||||
|
||||
// // GET: fetch volunteers, addresses, and existing assignments
|
||||
// volunteers, err := fetchVolunteers()
|
||||
// if err != nil {
|
||||
// log.Println("fetch volunteers error:", err)
|
||||
// http.Error(w, "Failed to load volunteers", http.StatusInternalServerError)
|
||||
// return
|
||||
// }
|
||||
// addresses, err := fetchAddresses()
|
||||
// if err != nil {
|
||||
// log.Println("fetch addresses error:", err)
|
||||
// http.Error(w, "Failed to load addresses", http.StatusInternalServerError)
|
||||
// return
|
||||
// }
|
||||
// assignments, err := fetchAssignments()
|
||||
// if err != nil {
|
||||
// log.Println("fetch assignments error:", err)
|
||||
// http.Error(w, "Failed to load assignments", http.StatusInternalServerError)
|
||||
// return
|
||||
// }
|
||||
|
||||
// utils.Render(w, "assignments.html", map[string]interface{}{
|
||||
// "Title": "Admin — Assign Addresses",
|
||||
// "IsAuthenticated": true,
|
||||
// "ActiveSection": "admin_assignments",
|
||||
// "Volunteers": volunteers,
|
||||
// "Addresses": addresses,
|
||||
// "Assignments": assignments,
|
||||
// })
|
||||
// }
|
||||
|
||||
// // GET (edit form) + POST (update/delete)
|
||||
// func AdminAssignmentEditHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// idStr := r.URL.Query().Get("id")
|
||||
// id, _ := strconv.Atoi(idStr)
|
||||
// if id <= 0 {
|
||||
// http.NotFound(w, r)
|
||||
// return
|
||||
// }
|
||||
|
||||
// if r.Method == http.MethodPost {
|
||||
// action := r.FormValue("action")
|
||||
// switch action {
|
||||
// case "delete":
|
||||
// if err := deleteAssignment(id); err != nil {
|
||||
// log.Println("delete assignment error:", err)
|
||||
// http.Error(w, "Failed to delete assignment", http.StatusInternalServerError)
|
||||
// return
|
||||
// }
|
||||
// http.Redirect(w, r, "/admin/assignments", http.StatusSeeOther)
|
||||
// return
|
||||
// case "update":
|
||||
// if err := updateAssignmentFromForm(id, r); err != nil {
|
||||
// log.Println("update assignment error:", err)
|
||||
// vm, _ := fetchAssignmentByID(id)
|
||||
// volunteers, _ := fetchVolunteers()
|
||||
// addresses, _ := fetchAddresses()
|
||||
|
||||
// utils.Render(w, "assignment_edit.html", map[string]interface{}{
|
||||
// "Title": "Edit Assignment",
|
||||
// "Assignment": vm,
|
||||
// "Volunteers": volunteers,
|
||||
// "Addresses": addresses,
|
||||
// "Error": err.Error(),
|
||||
// })
|
||||
// return
|
||||
// }
|
||||
// http.Redirect(w, r, "/admin/assignments", http.StatusSeeOther)
|
||||
// return
|
||||
// default:
|
||||
// http.Error(w, "Unknown action", http.StatusBadRequest)
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
||||
// // GET edit
|
||||
// vm, err := fetchAssignmentByID(id)
|
||||
// if err != nil {
|
||||
// if err == sql.ErrNoRows {
|
||||
// http.NotFound(w, r)
|
||||
// return
|
||||
// }
|
||||
// log.Println("fetch assignment by ID error:", err)
|
||||
// http.Error(w, "Failed to load assignment", http.StatusInternalServerError)
|
||||
// return
|
||||
// }
|
||||
|
||||
// volunteers, err := fetchVolunteers()
|
||||
// if err != nil {
|
||||
// log.Println("fetch volunteers error:", err)
|
||||
// http.Error(w, "Failed to load volunteers", http.StatusInternalServerError)
|
||||
// return
|
||||
// }
|
||||
// addresses, err := fetchAddresses()
|
||||
// if err != nil {
|
||||
// log.Println("fetch addresses error:", err)
|
||||
// http.Error(w, "Failed to load addresses", http.StatusInternalServerError)
|
||||
// return
|
||||
// }
|
||||
|
||||
// utils.Render(w, "assignment_edit.html", map[string]interface{}{
|
||||
// "Title": "Edit Assignment",
|
||||
// "Assignment": vm,
|
||||
// "Volunteers": volunteers,
|
||||
// "Addresses": addresses,
|
||||
// })
|
||||
// }
|
||||
|
||||
// // ----- Helpers -----
|
||||
|
||||
// func createAssignmentFromForm(r *http.Request) error {
|
||||
// volID, _ := strconv.Atoi(r.FormValue("volunteer_id"))
|
||||
// addrID, _ := strconv.Atoi(r.FormValue("address_id"))
|
||||
// dateStr := r.FormValue("date")
|
||||
// timeStr := r.FormValue("appointment_time")
|
||||
|
||||
// if volID <= 0 || addrID <= 0 || dateStr == "" || timeStr == "" {
|
||||
// return errors.New("please fill all required fields")
|
||||
// }
|
||||
|
||||
// if _, err := time.Parse("2006-01-02", dateStr); err != nil {
|
||||
// return errors.New("invalid date format")
|
||||
// }
|
||||
// if _, err := time.Parse("15:04", timeStr); err != nil {
|
||||
// return errors.New("invalid time format")
|
||||
// }
|
||||
|
||||
// _, err := models.DB.Exec(`
|
||||
// INSERT INTO schedual (user_id, address_id, appointment_date, appointment_time, created_at, updated_at)
|
||||
// VALUES ($1,$2,$3,$4,NOW(),NOW())
|
||||
// `, volID, addrID, dateStr, timeStr)
|
||||
|
||||
// if err != nil {
|
||||
// log.Println("database insert error:", err)
|
||||
// return errors.New("failed to create assignment")
|
||||
// }
|
||||
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// func updateAssignmentFromForm(id int, r *http.Request) error {
|
||||
// volID, _ := strconv.Atoi(r.FormValue("volunteer_id"))
|
||||
// addrID, _ := strconv.Atoi(r.FormValue("address_id"))
|
||||
// dateStr := r.FormValue("date")
|
||||
// timeStr := r.FormValue("appointment_time")
|
||||
|
||||
// if volID <= 0 || addrID <= 0 || dateStr == "" || timeStr == "" {
|
||||
// return errors.New("please fill all required fields")
|
||||
// }
|
||||
|
||||
// if _, err := time.Parse("2006-01-02", dateStr); err != nil {
|
||||
// return errors.New("invalid date format")
|
||||
// }
|
||||
// if _, err := time.Parse("15:04", timeStr); err != nil {
|
||||
// return errors.New("invalid time format")
|
||||
// }
|
||||
|
||||
// result, err := models.DB.Exec(`
|
||||
// UPDATE schedual
|
||||
// SET user_id=$1, address_id=$2, appointment_date=$3, appointment_time=$4, updated_at=NOW()
|
||||
// WHERE schedual_id=$5
|
||||
// `, volID, addrID, dateStr, timeStr, id)
|
||||
|
||||
// if err != nil {
|
||||
// log.Println("database update error:", err)
|
||||
// return errors.New("failed to update assignment")
|
||||
// }
|
||||
|
||||
// rowsAffected, _ := result.RowsAffected()
|
||||
// if rowsAffected == 0 {
|
||||
// return errors.New("assignment not found")
|
||||
// }
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// func deleteAssignment(id int) error {
|
||||
// result, err := models.DB.Exec(`DELETE FROM schedual WHERE schedual_id=$1`, id)
|
||||
// if err != nil {
|
||||
// log.Println("database delete error:", err)
|
||||
// return errors.New("failed to delete assignment")
|
||||
// }
|
||||
// rowsAffected, _ := result.RowsAffected()
|
||||
// if rowsAffected == 0 {
|
||||
// return errors.New("assignment not found")
|
||||
// }
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// // Fetch volunteers
|
||||
// type VolunteerPick struct {
|
||||
// ID int
|
||||
// FirstName string
|
||||
// LastName string
|
||||
// Email string
|
||||
// }
|
||||
|
||||
// func fetchVolunteers() ([]VolunteerPick, error) {
|
||||
// rows, err := models.DB.Query(`
|
||||
// SELECT users_id, first_name, last_name, email
|
||||
// FROM "user"
|
||||
// WHERE role='volunteer'
|
||||
// ORDER BY first_name, last_name
|
||||
// `)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// defer rows.Close()
|
||||
|
||||
// var out []VolunteerPick
|
||||
// for rows.Next() {
|
||||
// var v VolunteerPick
|
||||
// if err := rows.Scan(&v.ID, &v.FirstName, &v.LastName, &v.Email); err != nil {
|
||||
// log.Println("fetchVolunteers scan:", err)
|
||||
// continue
|
||||
// }
|
||||
// out = append(out, v)
|
||||
// }
|
||||
// return out, rows.Err()
|
||||
// }
|
||||
|
||||
// // Fetch addresses
|
||||
// type AddressPick struct {
|
||||
// ID int
|
||||
// Label string
|
||||
// VisitedValidated bool
|
||||
// }
|
||||
|
||||
// func fetchAddresses() ([]AddressPick, error) {
|
||||
// rows, err := models.DB.Query(`
|
||||
// SELECT
|
||||
// address_id,
|
||||
// address,
|
||||
// street_name,
|
||||
// street_type,
|
||||
// street_quadrant,
|
||||
// house_number,
|
||||
// house_alpha,
|
||||
// longitude,
|
||||
// latitude,
|
||||
// visited_validated
|
||||
// FROM address_database
|
||||
// ORDER BY address_id DESC
|
||||
// `)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// defer rows.Close()
|
||||
|
||||
// var out []AddressPick
|
||||
// for rows.Next() {
|
||||
// var addr models.AddressDatabase
|
||||
// if err := rows.Scan(
|
||||
// &addr.AddressID,
|
||||
// &addr.Address,
|
||||
// &addr.StreetName,
|
||||
// &addr.StreetType,
|
||||
// &addr.StreetQuadrant,
|
||||
// &addr.HouseNumber,
|
||||
// &addr.HouseAlpha,
|
||||
// &addr.Longitude,
|
||||
// &addr.Latitude,
|
||||
// &addr.VisitedValidated,
|
||||
// ); err != nil {
|
||||
// log.Println("fetchAddresses scan:", err)
|
||||
// continue
|
||||
// }
|
||||
|
||||
// label := addr.Address
|
||||
// if label == "" {
|
||||
// label = addr.HouseNumber
|
||||
// if addr.StreetName != "" {
|
||||
// if label != "" {
|
||||
// label += " "
|
||||
// }
|
||||
// label += addr.StreetName
|
||||
// }
|
||||
// if addr.StreetType != "" {
|
||||
// label += " " + addr.StreetType
|
||||
// }
|
||||
// if addr.StreetQuadrant != "" {
|
||||
// label += " " + addr.StreetQuadrant
|
||||
// }
|
||||
// if addr.HouseAlpha != nil {
|
||||
// label += " " + *addr.HouseAlpha
|
||||
// }
|
||||
// }
|
||||
|
||||
// out = append(out, AddressPick{
|
||||
// ID: addr.AddressID,
|
||||
// Label: label,
|
||||
// VisitedValidated: addr.VisitedValidated,
|
||||
// })
|
||||
// }
|
||||
// return out, rows.Err()
|
||||
// }
|
||||
|
||||
// // Add this missing function
|
||||
// func fetchAssignments() ([]AssignmentVM, error) {
|
||||
// rows, err := models.DB.Query(`
|
||||
// SELECT
|
||||
// s.schedual_id,
|
||||
// u.users_id,
|
||||
// COALESCE(u.first_name,'') || ' ' || COALESCE(u.last_name,'') AS volunteer_name,
|
||||
// a.address_id,
|
||||
// COALESCE(a.address,'') AS address,
|
||||
// s.appointment_date,
|
||||
// s.appointment_time
|
||||
// FROM schedual s
|
||||
// JOIN "user" u ON u.users_id = s.user_id
|
||||
// JOIN address_database a ON a.address_id = s.address_id
|
||||
// ORDER BY s.appointment_date DESC, s.appointment_time DESC
|
||||
// `)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// defer rows.Close()
|
||||
|
||||
// var assignments []AssignmentVM
|
||||
// for rows.Next() {
|
||||
// var vm AssignmentVM
|
||||
// if err := rows.Scan(&vm.ID, &vm.VolunteerID, &vm.VolunteerName, &vm.AddressID, &vm.Address,
|
||||
// &vm.Date, &vm.AppointmentTime); err != nil {
|
||||
// log.Println("fetchAssignments scan:", err)
|
||||
// continue
|
||||
// }
|
||||
// assignments = append(assignments, vm)
|
||||
// }
|
||||
// return assignments, rows.Err()
|
||||
// }
|
||||
|
||||
// func fetchAssignmentByID(id int) (AssignmentVM, error) {
|
||||
// var vm AssignmentVM
|
||||
// err := models.DB.QueryRow(`
|
||||
// SELECT
|
||||
// s.schedual_id,
|
||||
// u.users_id,
|
||||
// COALESCE(u.first_name,'') || ' ' || COALESCE(u.last_name,'') AS volunteer_name,
|
||||
// a.address_id,
|
||||
// COALESCE(a.address,'') AS address,
|
||||
// s.appointment_date,
|
||||
// s.appointment_time
|
||||
// FROM schedual s
|
||||
// JOIN "user" u ON u.users_id = s.user_id
|
||||
// JOIN address_database a ON a.address_id = s.address_id
|
||||
// WHERE s.schedual_id = $1
|
||||
// `, id).Scan(&vm.ID, &vm.VolunteerID, &vm.VolunteerName, &vm.AddressID, &vm.Address,
|
||||
// &vm.Date, &vm.AppointmentTime)
|
||||
|
||||
// return vm, err
|
||||
// }
|
||||
83
Example_code/admin_apointment.go
Normal file
83
Example_code/admin_apointment.go
Normal file
@@ -0,0 +1,83 @@
|
||||
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",
|
||||
})
|
||||
}
|
||||
@@ -75,11 +75,11 @@ func VolunteerAppointmentHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// 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"
|
||||
a.PollButtonText = "Poll"
|
||||
a.PollButtonClass = "px-1 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"
|
||||
a.PollButtonText = "Ask"
|
||||
a.PollButtonClass = "px-1 py-1 bg-blue-600 text-white text-sm hover:bg-blue-700 rounded"
|
||||
}
|
||||
|
||||
appointments = append(appointments, a)
|
||||
|
||||
@@ -150,7 +150,6 @@ func getVolunteerStatistics(userID int) (*VolunteerStatistics, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Print("Stats: ", stats.AppointmentsThisWeek," Today's date: " , today ,"fasd", weekStart)
|
||||
|
||||
|
||||
// Total appointments
|
||||
|
||||
@@ -1,25 +1,5 @@
|
||||
{{ define "content" }}
|
||||
<div class="flex-1 flex flex-col overflow-hidden">
|
||||
<!-- Top Navigation -->
|
||||
<div class="bg-white border-b border-gray-200 px-6 py-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<i
|
||||
class="{{if .PageIcon}}{{.PageIcon}}{{else}}fas fa-map-marker-alt{{end}} text-green-600"
|
||||
></i>
|
||||
<span class="text-sm font-medium"> Address Database </span>
|
||||
</div>
|
||||
</div>
|
||||
{{if .Pagination}}
|
||||
<div class="text-sm text-gray-600">
|
||||
Showing {{.Pagination.StartRecord}}-{{.Pagination.EndRecord}} of
|
||||
{{.Pagination.TotalRecords}} addresses
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Toolbar -->
|
||||
<div class="bg-gray-50 border-b border-gray-200 px-6 py-3">
|
||||
<div class="flex items-center justify-between">
|
||||
|
||||
@@ -1,19 +1,5 @@
|
||||
{{ define "content" }}
|
||||
<div class="flex-1 flex flex-col overflow-hidden">
|
||||
<!-- Top Navigation -->
|
||||
<div class="bg-white border-b border-gray-200 px-6 py-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<i
|
||||
class="{{if .PageIcon}}{{.PageIcon}}{{else}}fas fa-calendar-alt{{end}} text-green-600"
|
||||
></i>
|
||||
<span class="text-sm font-medium">Appointments</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Toolbar -->
|
||||
<div class="bg-gray-50 border-b border-gray-200 px-6 py-3">
|
||||
<div class="flex items-center justify-between">
|
||||
@@ -41,16 +27,28 @@
|
||||
<tr
|
||||
class="text-left text-gray-700 font-medium border-b border-gray-200"
|
||||
>
|
||||
<th class="px-6 py-3 whitespace-nowrap">Poll</th>
|
||||
<th class="px-6 py-3 whitespace-nowrap">Address</th>
|
||||
<th class="px-6 py-3 whitespace-nowrap">Coordinates</th>
|
||||
<th class="px-6 py-3 whitespace-nowrap">Appointment Date</th>
|
||||
<th class="px-6 py-3 whitespace-nowrap">Appointment Time</th>
|
||||
<th class="px-6 py-3 whitespace-nowrap">Poll Question</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200">
|
||||
{{ range .Appointments }}
|
||||
<tr class="hover:bg-gray-50">
|
||||
<td class="px-6 py-3 whitespace-nowrap">
|
||||
{{ if .HasPollResponse }}
|
||||
<span class="{{ .PollButtonClass }}"> {{ .PollButtonText }} </span>
|
||||
{{ else }}
|
||||
<a
|
||||
href="/poll?address_id={{ .AddressID }}"
|
||||
class="{{ .PollButtonClass }}"
|
||||
>
|
||||
{{ .PollButtonText }}
|
||||
</a>
|
||||
{{ end }}
|
||||
</td>
|
||||
<td class="px-6 py-3 whitespace-nowrap">{{ .Address }}</td>
|
||||
<td class="px-6 py-3 whitespace-nowrap">
|
||||
<a
|
||||
@@ -67,18 +65,6 @@
|
||||
<td class="px-6 py-3 whitespace-nowrap">
|
||||
{{ .AppointmentTime.Format "15:04" }}
|
||||
</td>
|
||||
<td class="px-6 py-3 whitespace-nowrap">
|
||||
{{ if .HasPollResponse }}
|
||||
<span class="{{ .PollButtonClass }}"> {{ .PollButtonText }} </span>
|
||||
{{ else }}
|
||||
<a
|
||||
href="/poll?address_id={{ .AddressID }}"
|
||||
class="{{ .PollButtonClass }}"
|
||||
>
|
||||
{{ .PollButtonText }}
|
||||
</a>
|
||||
{{ end }}
|
||||
</td>
|
||||
</tr>
|
||||
{{ else }}
|
||||
<tr>
|
||||
|
||||
@@ -18,40 +18,6 @@
|
||||
<body class="bg-gray-50">
|
||||
<!-- Full Width Container -->
|
||||
<div class="min-h-screen w-full flex flex-col">
|
||||
<!-- Top Navigation Bar -->
|
||||
<div class="bg-white border-b border-gray-200 w-full">
|
||||
<div class="px-8 py-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-8 h-8 bg-blue-600 flex items-center justify-center">
|
||||
<i class="fas fa-chart-bar text-white text-sm"></i>
|
||||
</div>
|
||||
<span class="text-xl font-semibold text-gray-900">
|
||||
Dashboard Overview
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<button
|
||||
class="px-6 py-2.5 bg-blue-600 text-white text-sm font-medium hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
<i class="fas fa-download mr-2"></i>Export Data
|
||||
</button>
|
||||
<button
|
||||
class="px-6 py-2.5 bg-green-600 text-white text-sm font-medium hover:bg-green-700 transition-colors"
|
||||
onclick="window.location.href='/addresses/upload-csv'"
|
||||
>
|
||||
<i class="fas fa-upload mr-2"></i>Import Data
|
||||
</button>
|
||||
<button
|
||||
class="px-6 py-2.5 border border-gray-300 text-gray-700 text-sm font-medium hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<i class="fas fa-filter mr-2"></i>Filter
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Dashboard Content -->
|
||||
<div class="w-full">
|
||||
<!-- Stats Grid - Full Width -->
|
||||
|
||||
@@ -1,29 +1,18 @@
|
||||
{{ define "content" }}
|
||||
<div class="flex-1 flex flex-col overflow-hidden">
|
||||
<!-- Top Navigation -->
|
||||
<div class="bg-white border-b border-gray-200 px-4 sm:px-6 py-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<i class="fas fa-tachometer-alt text-green-600"></i>
|
||||
<span class="text-sm font-medium">Volunteer Dashboard</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="flex-1 overflow-hidden bg-gray-50">
|
||||
<div class="h-full flex flex-col lg:flex-row gap-6 p-4 sm:p-6">
|
||||
<div class="h-screen flex flex-col lg:flex-row gap-6 p-4 sm:p-6">
|
||||
<!-- Left Column - Posts -->
|
||||
<div class="flex-1 lg:flex-none lg:w-2/3 space-y-0">
|
||||
<div class="flex-1 lg:flex-none lg:w-1/3 overflow-y-auto pr-2">
|
||||
{{ if .Posts }}{{range .Posts}}
|
||||
<!-- Posts Feed -->
|
||||
<article class="bg-white border-b border-gray-200">
|
||||
<!-- Post Header -->
|
||||
<div class="flex items-center px-4 sm:px-6 py-4">
|
||||
<div class="flex items-center px-6 py-4">
|
||||
<div class="flex-shrink-0">
|
||||
<div
|
||||
class="w-10 h-10 bg-blue-500 flex items-center justify-center text-white font-semibold rounded-full"
|
||||
class="w-10 h-10 bg-blue-500 flex items-center justify-center text-white font-semibold"
|
||||
>
|
||||
{{slice .AuthorName 0 1}}
|
||||
</div>
|
||||
@@ -50,7 +39,7 @@
|
||||
|
||||
<!-- Post Content -->
|
||||
{{if .Content}}
|
||||
<div class="px-4 sm:px-6 pt-2 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>
|
||||
@@ -58,7 +47,7 @@
|
||||
{{end}}
|
||||
</article>
|
||||
{{else}}
|
||||
<div class="bg-white p-8 sm:p-12 text-center border-b border-gray-200">
|
||||
<div class="bg-white p-12 text-center">
|
||||
<div class="max-w-sm mx-auto">
|
||||
<svg
|
||||
class="w-16 h-16 mx-auto text-gray-300 mb-4"
|
||||
@@ -74,6 +63,9 @@
|
||||
></path>
|
||||
</svg>
|
||||
<h3 class="text-lg font-medium text-gray-900 mb-2">No posts yet</h3>
|
||||
<p class="text-gray-500">
|
||||
Be the first to share something with the community!
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }} {{ else }}
|
||||
@@ -94,7 +86,163 @@
|
||||
</div>
|
||||
|
||||
<!-- Right Column - Statistics -->
|
||||
<div class="w-full lg:w-1/3 flex flex-col gap-4 sm:gap-6">
|
||||
<div
|
||||
class="w-full lg:w-1/3 flex flex-col gap-4 sm:gap-6 sticky top-0 self-start h-fit"
|
||||
>
|
||||
<!-- Today's Overview -->
|
||||
<div class="bg-white border-b border-gray-200">
|
||||
<div class="px-4 sm:px-6 py-4">
|
||||
<h3 class="text-sm font-semibold text-gray-900 mb-4">
|
||||
Today's Overview
|
||||
</h3>
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
class="w-8 h-8 bg-gray-100 rounded-full flex items-center justify-center flex-shrink-0"
|
||||
>
|
||||
<i class="fas fa-calendar-day text-gray-600 text-xs"></i>
|
||||
</div>
|
||||
<span class="text-sm text-gray-700">Appointments Today</span>
|
||||
</div>
|
||||
<span class="text-lg font-semibold text-gray-900">
|
||||
{{ .Statistics.AppointmentsToday }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
class="w-8 h-8 bg-gray-100 rounded-full flex items-center justify-center flex-shrink-0"
|
||||
>
|
||||
<i class="fas fa-calendar-week text-gray-600 text-xs"></i>
|
||||
</div>
|
||||
<span class="text-sm text-gray-700"
|
||||
>Appointments Tomorrow</span
|
||||
>
|
||||
</div>
|
||||
<span class="text-lg font-semibold text-gray-900">
|
||||
{{ .Statistics.AppointmentsTomorrow }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
class="w-8 h-8 bg-gray-100 rounded-full flex items-center justify-center flex-shrink-0"
|
||||
>
|
||||
<i class="fas fa-calendar-week text-gray-600 text-xs"></i>
|
||||
</div>
|
||||
<span class="text-sm text-gray-700">This Week</span>
|
||||
</div>
|
||||
<span class="text-lg font-semibold text-gray-900">
|
||||
{{ .Statistics.AppointmentsThisWeek }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Polling Progress -->
|
||||
<div class="bg-white border-b border-gray-200">
|
||||
<div class="px-4 sm:px-6 py-4">
|
||||
<h3 class="text-sm font-semibold text-gray-900 mb-4">
|
||||
Polling Progress
|
||||
</h3>
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
class="w-8 h-8 bg-gray-100 rounded-full flex items-center justify-center flex-shrink-0"
|
||||
>
|
||||
<i class="fas fa-check-circle text-green-600 text-xs"></i>
|
||||
</div>
|
||||
<span class="text-sm text-gray-700">Polls Completed</span>
|
||||
</div>
|
||||
<span class="text-lg font-semibold text-green-600">
|
||||
{{ .Statistics.PollsCompleted }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
class="w-8 h-8 bg-gray-100 rounded-full flex items-center justify-center flex-shrink-0"
|
||||
>
|
||||
<i class="fas fa-clock text-orange-600 text-xs"></i>
|
||||
</div>
|
||||
<span class="text-sm text-gray-700">Polls Remaining</span>
|
||||
</div>
|
||||
<span class="text-lg font-semibold text-orange-600">
|
||||
{{ .Statistics.PollsRemaining }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Progress Bar -->
|
||||
{{ if gt .Statistics.TotalAppointments 0 }}
|
||||
<div class="mt-4">
|
||||
<div class="flex justify-between text-xs text-gray-600 mb-2">
|
||||
<span>Progress</span>
|
||||
<span
|
||||
>{{ .Statistics.PollsCompleted }}/{{
|
||||
.Statistics.TotalAppointments }}</span
|
||||
>
|
||||
</div>
|
||||
<div class="w-full bg-gray-200 rounded-full h-2">
|
||||
<div
|
||||
class="bg-gray-600 h-2 rounded-full transition-all duration-300"
|
||||
style="width: {{ .Statistics.PollCompletionPercent }}%"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Signs Summary -->
|
||||
<div class="bg-white border-b border-gray-200">
|
||||
<div class="px-4 sm:px-6 py-4">
|
||||
<h3 class="text-sm font-semibold text-gray-900 mb-4">
|
||||
Signs Requested
|
||||
</h3>
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
class="w-8 h-8 bg-gray-100 rounded-full flex items-center justify-center flex-shrink-0"
|
||||
>
|
||||
<i class="fas fa-sign text-gray-600 text-xs"></i>
|
||||
</div>
|
||||
<span class="text-sm text-gray-700">Lawn Signs</span>
|
||||
</div>
|
||||
<span class="text-lg font-semibold text-gray-900">
|
||||
{{ .Statistics.LawnSignsRequested }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
class="w-8 h-8 bg-gray-100 rounded-full flex items-center justify-center flex-shrink-0"
|
||||
>
|
||||
<i class="fas fa-flag text-gray-600 text-xs"></i>
|
||||
</div>
|
||||
<span class="text-sm text-gray-700">Banner Signs</span>
|
||||
</div>
|
||||
<span class="text-lg font-semibold text-gray-900">
|
||||
{{ .Statistics.BannerSignsRequested }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right Column - Statistics -->
|
||||
<div
|
||||
class="w-full lg:w-1/3 flex flex-col gap-4 sm:gap-6 sticky top-0 self-start h-fit"
|
||||
>
|
||||
<!-- Today's Overview -->
|
||||
<div class="bg-white border-b border-gray-200">
|
||||
<div class="px-4 sm:px-6 py-4">
|
||||
|
||||
@@ -14,172 +14,185 @@
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"
|
||||
/>
|
||||
</head>
|
||||
<body class="bg-white font-sans">
|
||||
<body class="bg-gray-50 font-sans">
|
||||
{{ if .IsAuthenticated }}
|
||||
<!-- Authenticated User Interface -->
|
||||
<div class="w-full h-screen bg-white overflow-hidden" x-data="{ sidebarOpen: false }">
|
||||
<!-- Mobile Header -->
|
||||
<div class="lg:hidden bg-gray-100 px-4 py-3 flex items-center justify-between border-b border-gray-200">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-5 h-5 bg-orange-500 rounded text-white text-xs flex items-center justify-center font-bold">
|
||||
L
|
||||
</div>
|
||||
<span class="text-sm font-medium">Poll System</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<button @click="sidebarOpen = !sidebarOpen" class="p-2 hover:bg-gray-200 rounded">
|
||||
<i class="fas fa-bars text-gray-600"></i>
|
||||
</button>
|
||||
<span class="text-sm font-medium text-gray-600">{{.UserName}}</span>
|
||||
<div class="w-9 h-9 bg-blue-500 flex items-center justify-center text-white font-semibold">
|
||||
{{slice .UserName 0 1}}
|
||||
</div>
|
||||
<a href="/logout" class="p-2 hover:bg-gray-200 rounded">
|
||||
<i class="fas fa-external-link-alt text-gray-500"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="min-h-screen" x-data="{ mobileMenuOpen: false }">
|
||||
|
||||
<!-- Desktop Title Bar -->
|
||||
<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 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>
|
||||
<!-- Simple Navigation Bar -->
|
||||
<nav class="bg-gray-700 border-b border-gray-600 fixed top-0 left-0 w-full z-50">
|
||||
<div class="max-w-9xl mx-auto px-4 sm:px-6 lg:px-8 ">
|
||||
<div class="flex justify-between items-center h-14 ">
|
||||
|
||||
<!-- Right Side: User Info -->
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="text-sm text-gray-600 hidden sm:block">Welcome back, {{ .UserName }}!</span>
|
||||
<div class="w-9 h-9 bg-blue-500 flex items-center justify-center text-white font-semibold">
|
||||
{{slice .UserName 0 1}}
|
||||
</div>
|
||||
<a href="/logout" class="p-2 hover:bg-gray-200 rounded">
|
||||
<i class="fas fa-external-link-alt text-gray-500"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex h-full">
|
||||
<!-- Mobile Sidebar Overlay -->
|
||||
<div x-show="sidebarOpen"
|
||||
x-transition:enter="transition-opacity ease-linear duration-300"
|
||||
x-transition:enter-start="opacity-0"
|
||||
x-transition:enter-end="opacity-100"
|
||||
x-transition:leave="transition-opacity ease-linear duration-300"
|
||||
x-transition:leave-start="opacity-100"
|
||||
x-transition:leave-end="opacity-0"
|
||||
class="fixed inset-0 bg-gray-600 bg-opacity-75 z-20 lg:hidden"
|
||||
@click="sidebarOpen = false">
|
||||
</div>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<div class="fixed inset-y-0 left-0 w-64 bg-gray-50 border-r border-gray-200 transform transition-transform duration-300 ease-in-out z-30 lg:relative lg:translate-x-0 lg:z-0"
|
||||
:class="sidebarOpen ? 'translate-x-0' : '-translate-x-full'"
|
||||
x-show="sidebarOpen || window.innerWidth >= 1024"
|
||||
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-start="translate-x-0"
|
||||
x-transition:leave-end="-translate-x-full">
|
||||
|
||||
<!-- Mobile Close Button -->
|
||||
<div class="lg:hidden flex justify-between items-center p-4 border-b border-gray-200">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-5 h-5 bg-orange-500 rounded text-white text-xs flex items-center justify-center font-bold">
|
||||
L
|
||||
<!-- Left: Logo and Navigation Links -->
|
||||
<div class="flex items-center space-x-12">
|
||||
<!-- Logo -->
|
||||
<div class="flex items-center space-x-4">
|
||||
<img src="../../static/icon-512.png" alt="Logo" class="w-6 h-6"/>
|
||||
<span class="text-xl font-semibold text-white">Poll System</span>
|
||||
</div>
|
||||
<span class="text-sm font-medium">Poll System</span>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Desktop Navigation Links -->
|
||||
<div class="hidden md:flex items-center space-x-10">
|
||||
{{ if .ShowAdminNav }}
|
||||
<a href="/dashboard" class="text-sm font-medium {{if eq .ActiveSection "dashboard"}}text-blue-300 border-b-2 border-blue-300{{else}}text-gray-300 hover:text-white{{end}} py-3 px-1">
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="/volunteers" class="text-sm font-medium {{if eq .ActiveSection "volunteer"}}text-blue-300 border-b-2 border-blue-300{{else}}text-gray-300 hover:text-white{{end}} py-3 px-1">
|
||||
Volunteers
|
||||
</a>
|
||||
<a href="/team_builder" class="text-sm font-medium {{if eq .ActiveSection "team_builder"}}text-blue-300 border-b-2 border-blue-300{{else}}text-gray-300 hover:text-white{{end}} py-3 px-1">
|
||||
Team Builder
|
||||
</a>
|
||||
<a href="/addresses" class="text-sm font-medium {{if eq .ActiveSection "address"}}text-blue-300 border-b-2 border-blue-300{{else}}text-gray-300 hover:text-white{{end}} py-3 px-1">
|
||||
Addresses
|
||||
</a>
|
||||
<a href="/posts" class="text-sm font-medium {{if eq .ActiveSection "post"}}text-blue-300 border-b-2 border-blue-300{{else}}text-gray-300 hover:text-white{{end}} py-3 px-1">
|
||||
Posts
|
||||
</a>
|
||||
<a href="/reports" class="text-sm font-medium {{if eq .ActiveSection "report"}}text-blue-300 border-b-2 border-blue-300{{else}}text-gray-300 hover:text-white{{end}} py-3 px-1">
|
||||
Reports
|
||||
</a>
|
||||
{{ end }}
|
||||
|
||||
{{ if .ShowVolunteerNav }}
|
||||
<a href="/volunteer/dashboard" class="text-sm font-medium {{if eq .ActiveSection "dashboard"}}text-blue-300 border-b-2 border-blue-300{{else}}text-gray-300 hover:text-white{{end}} py-3 px-1">
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="/volunteer/Addresses" class="text-sm font-medium {{if eq .ActiveSection "address"}}text-blue-300 border-b-2 border-blue-300{{else}}text-gray-300 hover:text-white{{end}} py-3 px-1">
|
||||
Assigned Address
|
||||
</a>
|
||||
<a href="/volunteer/schedual" class="text-sm font-medium {{if eq .ActiveSection "schedual"}}text-blue-300 border-b-2 border-blue-300{{else}}text-gray-300 hover:text-white{{end}} py-3 px-1">
|
||||
My Schedule
|
||||
</a>
|
||||
{{ end }}
|
||||
|
||||
<a href="/profile" class="text-sm font-medium {{if eq .ActiveSection "profile"}}text-blue-300 border-b-2 border-blue-300{{else}}text-gray-300 hover:text-white{{end}} py-3 px-1">
|
||||
Profile
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Right: User Info and Actions -->
|
||||
<div class="flex items-center space-x-4">
|
||||
<!-- User Avatar and Name -->
|
||||
<div class="hidden sm:flex items-center space-x-3">
|
||||
<span class="text-sm text-gray-300">{{.UserName}}</span>
|
||||
<div class="w-8 h-8 bg-blue-500 flex items-center justify-center text-white font-medium rounded-full">
|
||||
{{slice .UserName 0 1}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Logout Button -->
|
||||
<a href="/logout" class="hidden sm:flex items-center px-3 py-2 text-sm font-medium text-gray-300 hover:text-white hover:bg-gray-600 rounded-md">
|
||||
<i class="fas fa-sign-out-alt mr-2"></i>
|
||||
Logout
|
||||
</a>
|
||||
|
||||
<!-- Mobile Menu Button -->
|
||||
<button @click="mobileMenuOpen = !mobileMenuOpen" class="md:hidden p-2 rounded-md text-gray-300 hover:text-white hover:bg-gray-600">
|
||||
<i class="fas fa-bars" x-show="!mobileMenuOpen"></i>
|
||||
<i class="fas fa-times" x-show="mobileMenuOpen"></i>
|
||||
</button>
|
||||
</div>
|
||||
<button @click="sidebarOpen = false" class="p-1 hover:bg-gray-200 rounded">
|
||||
<i class="fas fa-times text-gray-500"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="p-3 space-y-4">
|
||||
<div class="space-y-1">
|
||||
{{ if .ShowAdminNav }}
|
||||
<a href="/dashboard"
|
||||
@click="sidebarOpen = false"
|
||||
class="flex items-center text-sm text-gray-600 hover:bg-gray-100 rounded px-2 py-1 {{if eq .ActiveSection "dashboard"}}bg-gray-100{{end}}">
|
||||
<i class="fas fa-chart-pie text-gray-400 mr-2"></i>
|
||||
<span>Dashboard</span>
|
||||
</a>
|
||||
<a href="/volunteers"
|
||||
@click="sidebarOpen = false"
|
||||
class="flex items-center text-sm text-gray-600 hover:bg-gray-100 rounded px-2 py-1 {{if eq .ActiveSection "volunteer"}}bg-gray-100{{end}}">
|
||||
<i class="fas fa-users text-gray-400 mr-2"></i>
|
||||
<span>Volunteers</span>
|
||||
</a>
|
||||
<a href="/team_builder"
|
||||
@click="sidebarOpen = false"
|
||||
class="flex items-center text-sm text-gray-600 hover:bg-gray-100 rounded px-2 py-1 {{if eq .ActiveSection "team_builder"}}bg-gray-100{{end}}">
|
||||
<i class="fas fa-user-friends text-gray-400 mr-2"></i>
|
||||
<span>Team Builder</span>
|
||||
</a>
|
||||
<a href="/addresses"
|
||||
@click="sidebarOpen = false"
|
||||
class="flex items-center text-sm text-gray-600 hover:bg-gray-100 rounded px-2 py-1 {{if eq .ActiveSection "address"}}bg-gray-100{{end}}">
|
||||
<i class="fas fa-map-marked-alt text-gray-400 mr-2"></i>
|
||||
<span>Addresses</span>
|
||||
</a>
|
||||
<a href="/posts"
|
||||
@click="sidebarOpen = false"
|
||||
class="flex items-center text-sm text-gray-600 hover:bg-gray-100 rounded px-2 py-1 {{if eq .ActiveSection "post"}}bg-gray-100{{end}}">
|
||||
<i class="fas fa-blog text-gray-400 mr-2"></i>
|
||||
<span>Posts</span>
|
||||
</a>
|
||||
<a href="/reports"
|
||||
@click="sidebarOpen = false"
|
||||
class="flex items-center text-sm text-gray-600 hover:bg-gray-100 rounded px-2 py-1 {{if eq .ActiveSection "report"}}bg-gray-100{{end}}">
|
||||
<i class="fas fa-table text-gray-400 mr-2"></i>
|
||||
<span>Reports</span>
|
||||
</a>
|
||||
{{ end }}
|
||||
<!-- Mobile Navigation 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-150"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-95"
|
||||
class="md:hidden border-t border-gray-600"
|
||||
@click.outside="mobileMenuOpen = false">
|
||||
|
||||
{{ if .ShowVolunteerNav }}
|
||||
<a href="/volunteer/dashboard"
|
||||
@click="sidebarOpen = false"
|
||||
class="flex items-center text-sm text-gray-600 hover:bg-gray-100 rounded px-2 py-1 {{if eq .ActiveSection "dashboard"}}bg-gray-100{{end}}">
|
||||
<i class="fas fa-chart-pie text-gray-400 mr-2"></i>
|
||||
<span>Dashboard</span>
|
||||
</a>
|
||||
<a href="/volunteer/Addresses"
|
||||
@click="sidebarOpen = false"
|
||||
class="flex items-center text-sm text-gray-600 hover:bg-gray-100 rounded px-2 py-1 {{if eq .ActiveSection "address"}}bg-gray-100{{end}}">
|
||||
<i class="fas fa-home text-gray-400 mr-2"></i>
|
||||
<span>Assigned Address</span>
|
||||
</a>
|
||||
<a href="/volunteer/schedual"
|
||||
@click="sidebarOpen = false"
|
||||
class="flex items-center text-sm text-gray-600 hover:bg-gray-100 rounded px-2 py-1 {{if eq .ActiveSection "schedual"}}bg-gray-100{{end}}">
|
||||
<i class="fas fa-calendar-alt text-gray-400 mr-2"></i>
|
||||
<span>My Schedule</span>
|
||||
</a>
|
||||
{{ end }}
|
||||
<div class="px-4 py-4 bg-gray-800">
|
||||
<!-- User Info Mobile -->
|
||||
<div class="flex items-center space-x-3 pb-4 mb-4 border-b border-gray-600">
|
||||
<div class="w-10 h-10 bg-blue-500 flex items-center justify-center text-white font-medium rounded-full">
|
||||
{{slice .UserName 0 1}}
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm font-medium text-white">{{.UserName}}</p>
|
||||
<p class="text-xs text-gray-400">Logged in</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="/profile"
|
||||
@click="sidebarOpen = false"
|
||||
class="flex items-center text-sm text-gray-600 hover:bg-gray-100 rounded px-2 py-1 {{if eq .ActiveSection "profile"}}bg-gray-100{{end}}">
|
||||
<i class="fas fa-user-circle text-gray-400 mr-2"></i>
|
||||
<span>Profile</span>
|
||||
</a>
|
||||
<!-- Mobile Navigation Links -->
|
||||
<div class="space-y-2">
|
||||
{{ if .ShowAdminNav }}
|
||||
<a href="/dashboard" @click="mobileMenuOpen = false"
|
||||
class="block px-3 py-2 rounded-md text-base font-medium {{if eq .ActiveSection "dashboard"}}text-blue-300 bg-gray-700{{else}}text-gray-300 hover:text-white hover:bg-gray-700{{end}}">
|
||||
<i class="fas fa-chart-pie mr-3 w-4"></i>Dashboard
|
||||
</a>
|
||||
<a href="/volunteers" @click="mobileMenuOpen = false"
|
||||
class="block px-3 py-2 rounded-md text-base font-medium {{if eq .ActiveSection "volunteer"}}text-blue-300 bg-gray-700{{else}}text-gray-300 hover:text-white hover:bg-gray-700{{end}}">
|
||||
<i class="fas fa-users mr-3 w-4"></i>Volunteers
|
||||
</a>
|
||||
<a href="/team_builder" @click="mobileMenuOpen = false"
|
||||
class="block px-3 py-2 rounded-md text-base font-medium {{if eq .ActiveSection "team_builder"}}text-blue-300 bg-gray-700{{else}}text-gray-300 hover:text-white hover:bg-gray-700{{end}}">
|
||||
<i class="fas fa-user-friends mr-3 w-4"></i>Team Builder
|
||||
</a>
|
||||
<a href="/addresses" @click="mobileMenuOpen = false"
|
||||
class="block px-3 py-2 rounded-md text-base font-medium {{if eq .ActiveSection "address"}}text-blue-300 bg-gray-700{{else}}text-gray-300 hover:text-white hover:bg-gray-700{{end}}">
|
||||
<i class="fas fa-map-marked-alt mr-3 w-4"></i>Addresses
|
||||
</a>
|
||||
<a href="/posts" @click="mobileMenuOpen = false"
|
||||
class="block px-3 py-2 rounded-md text-base font-medium {{if eq .ActiveSection "post"}}text-blue-300 bg-gray-700{{else}}text-gray-300 hover:text-white hover:bg-gray-700{{end}}">
|
||||
<i class="fas fa-blog mr-3 w-4"></i>Posts
|
||||
</a>
|
||||
<a href="/reports" @click="mobileMenuOpen = false"
|
||||
class="block px-3 py-2 rounded-md text-base font-medium {{if eq .ActiveSection "report"}}text-blue-300 bg-gray-700{{else}}text-gray-300 hover:text-white hover:bg-gray-700{{end}}">
|
||||
<i class="fas fa-table mr-3 w-4"></i>Reports
|
||||
</a>
|
||||
{{ end }}
|
||||
|
||||
{{ if .ShowVolunteerNav }}
|
||||
<a href="/volunteer/dashboard" @click="mobileMenuOpen = false"
|
||||
class="block px-3 py-2 rounded-md text-base font-medium {{if eq .ActiveSection "dashboard"}}text-blue-300 bg-gray-700{{else}}text-gray-300 hover:text-white hover:bg-gray-700{{end}}">
|
||||
<i class="fas fa-chart-pie mr-3 w-4"></i>Dashboard
|
||||
</a>
|
||||
<a href="/volunteer/Addresses" @click="mobileMenuOpen = false"
|
||||
class="block px-3 py-2 rounded-md text-base font-medium {{if eq .ActiveSection "address"}}text-blue-300 bg-gray-700{{else}}text-gray-300 hover:text-white hover:bg-gray-700{{end}}">
|
||||
<i class="fas fa-home mr-3 w-4"></i>Assigned Address
|
||||
</a>
|
||||
<a href="/volunteer/schedual" @click="mobileMenuOpen = false"
|
||||
class="block px-3 py-2 rounded-md text-base font-medium {{if eq .ActiveSection "schedual"}}text-blue-300 bg-gray-700{{else}}text-gray-300 hover:text-white hover:bg-gray-700{{end}}">
|
||||
<i class="fas fa-calendar-alt mr-3 w-4"></i>My Schedule
|
||||
</a>
|
||||
{{ end }}
|
||||
|
||||
<a href="/profile" @click="mobileMenuOpen = false"
|
||||
class="block px-3 py-2 rounded-md text-base font-medium {{if eq .ActiveSection "profile"}}text-blue-300 bg-gray-700{{else}}text-gray-300 hover:text-white hover:bg-gray-700{{end}}">
|
||||
<i class="fas fa-user-circle mr-3 w-4"></i>Profile
|
||||
</a>
|
||||
|
||||
<!-- Logout for Mobile -->
|
||||
<div class="border-t border-gray-600 pt-2 mt-4">
|
||||
<a href="/logout" class="block px-3 py-2 rounded-md text-base font-medium text-gray-300 hover:text-white hover:bg-gray-700">
|
||||
<i class="fas fa-sign-out-alt mr-3 w-4"></i>Logout
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="flex-1 flex flex-col overflow-hidden min-h-screen">
|
||||
<div class="bg-white flex-1 overflow-auto pb-[60px]">
|
||||
{{ template "content" . }}
|
||||
</div>
|
||||
<!-- Main Content Area -->
|
||||
<main class="flex-1 mt-14">
|
||||
<!--sm:px-4 lg:px-6-->
|
||||
<div class="max-w-9xl mx-auto overflow-hidden ">
|
||||
{{ template "content" . }}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
{{else}}
|
||||
<!-- Landing Page -->
|
||||
<!-- Landing Page (unchanged) -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -194,52 +207,52 @@
|
||||
<div class="min-h-screen" x-data="{ mobileMenuOpen: false }">
|
||||
|
||||
<!-- Navigation -->
|
||||
<nav class="bg-white border-b border-gray-200">
|
||||
<nav class="bg-gray-700 border-b border-gray-600">
|
||||
<div class="max-w-7xl mx-auto px-6">
|
||||
<div class="flex justify-between items-center h-16">
|
||||
<div class="flex justify-between items-center h-14">
|
||||
<!-- 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>
|
||||
<span class="text-xl font-semibold text-white">Linq</span>
|
||||
</div>
|
||||
|
||||
<!-- 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>
|
||||
<a href="#home" class="text-gray-300 hover:text-white py-3 px-1">Home</a>
|
||||
<a href="#products" class="text-gray-300 hover:text-white py-3 px-1">Products</a>
|
||||
<a href="#about" class="text-gray-300 hover:text-white py-3 px-1">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">
|
||||
<button onclick="openLoginModal()" class="px-4 py-2 text-gray-300 hover:text-white">
|
||||
Sign In
|
||||
</button>
|
||||
<button onclick="openRegisterModal()" class="px-6 py-2 bg-blue-600 text-white hover:bg-blue-700 rounded">
|
||||
<button onclick="openRegisterModal()" class="px-6 py-2 bg-blue-500 text-white hover:bg-blue-600 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 @click="mobileMenuOpen = !mobileMenuOpen" class="md:hidden p-2 text-gray-300 hover:text-white hover:bg-gray-600 rounded-md">
|
||||
<i class="fas fa-bars" x-show="!mobileMenuOpen"></i>
|
||||
<i class="fas fa-times" x-show="mobileMenuOpen"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Menu -->
|
||||
<div x-show="mobileMenuOpen" class="md:hidden border-t border-gray-200 bg-white">
|
||||
<div x-show="mobileMenuOpen" class="md:hidden border-t border-gray-600 bg-gray-800">
|
||||
<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">
|
||||
<a href="#home" @click="mobileMenuOpen = false" class="block py-2 text-gray-300 hover:text-white">Home</a>
|
||||
<a href="#products" @click="mobileMenuOpen = false" class="block py-2 text-gray-300 hover:text-white">Products</a>
|
||||
<a href="#about" @click="mobileMenuOpen = false" class="block py-2 text-gray-300 hover:text-white">About</a>
|
||||
<div class="border-t border-gray-600 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">
|
||||
class="block w-full text-left py-2 text-gray-300 hover:text-white">
|
||||
Sign In
|
||||
</button>
|
||||
<button onclick="openRegisterModal(); document.querySelector('[x-data]').__x.$data.mobileMenuOpen = false"
|
||||
class="block w-full py-2 bg-blue-600 text-white rounded">
|
||||
class="block w-full py-2 bg-blue-500 text-white hover:bg-blue-600 rounded">
|
||||
Get Started
|
||||
</button>
|
||||
</div>
|
||||
@@ -249,7 +262,7 @@
|
||||
</nav>
|
||||
|
||||
<!-- Hero -->
|
||||
<section id="home" class="pt-20 pb-16 px-6">
|
||||
<section id="home" class="pt-40 pb-16 px-6 min-h-[600px]">
|
||||
<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
|
||||
@@ -272,7 +285,7 @@
|
||||
</section>
|
||||
|
||||
<!-- Products Section -->
|
||||
<section id="products" class="py-16 bg-white">
|
||||
<section id="products" class="py-16 bg-white min-h-[600px]">
|
||||
<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>
|
||||
@@ -317,7 +330,7 @@
|
||||
</section>
|
||||
|
||||
<!-- About -->
|
||||
<section id="about" class="py-16 bg-gray-50">
|
||||
<section id="about" class="py-16 bg-gray-50 min-h-[600px]">
|
||||
<div class="max-w-6xl mx-auto px-6">
|
||||
<div class="grid lg:grid-cols-2 gap-12 items-center">
|
||||
<div>
|
||||
@@ -380,13 +393,7 @@
|
||||
<div class="flex flex-col lg:flex-row min-h-[500px]">
|
||||
<!-- Left Side - Image -->
|
||||
<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> -->
|
||||
<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">
|
||||
@@ -426,11 +433,6 @@
|
||||
<div class="flex flex-col lg:flex-row min-h-[600px]">
|
||||
<!-- Left Side - Image -->
|
||||
<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> -->
|
||||
<img src="../../static/feature-mobile1.jpg" alt="Welcome Image" class="object-cover h-full rounded-lg shadow-lg">
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,19 +1,6 @@
|
||||
{{ define "content" }}
|
||||
<div class="min-h-screen bg-gray-100">
|
||||
<!-- Header -->
|
||||
<!-- Top Navigation -->
|
||||
<div class="bg-white border-b border-gray-200 px-6 py-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<i
|
||||
class="{{if .PageIcon}}{{.PageIcon}}{{else}}fas fa-users{{end}} text-blue-600"
|
||||
></i>
|
||||
<span class="text-sm font-medium">Volunteer Management</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="max-w-2xl mx-auto">
|
||||
<!-- Create Post Form -->
|
||||
|
||||
@@ -1,18 +1,5 @@
|
||||
{{ define "content" }}
|
||||
<div class="min-h-screen bg-gray-50">
|
||||
<!-- Top Navigation -->
|
||||
<div class="bg-white border-b border-gray-200 px-6 py-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<i
|
||||
class="{{if .PageIcon}}{{.PageIcon}}{{else}}fas fa-users{{end}} text-blue-600"
|
||||
></i>
|
||||
<span class="text-sm font-medium">Volunteer Management</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Main Content -->
|
||||
<div class="p-6">
|
||||
<!-- Profile Info Section -->
|
||||
|
||||
@@ -17,30 +17,6 @@
|
||||
</head>
|
||||
<body class="bg-gray-100">
|
||||
<div class="flex-1 flex flex-col overflow-hidden">
|
||||
<!-- Top Navigation -->
|
||||
<div class="bg-white border-b border-gray-200 px-6 py-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
<i class="fas fa-chart-bar text-blue-600"></i>
|
||||
<span class="text-xl font-semibold text-gray-800">
|
||||
Schedual Overview
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<button
|
||||
class="px-5 py-2 bg-blue-600 text-white text-sm hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
<i class="fas fa-download mr-2"></i>Export Data
|
||||
</button>
|
||||
<button
|
||||
class="px-5 py-2 border border-gray-300 text-gray-700 text-sm hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<i class="fas fa-filter mr-2"></i>Filter
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Dashboard Content -->
|
||||
<div class="flex-1 overflow-auto">
|
||||
<!-- Top Stats Cards -->
|
||||
|
||||
@@ -2,20 +2,6 @@
|
||||
<div class="min-h-screen bg-gray-50">
|
||||
<!-- Header Bar -->
|
||||
|
||||
<!-- Top Navigation -->
|
||||
<div class="bg-white border-b border-gray-200 px-6 py-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<i
|
||||
class="{{if .PageIcon}}{{.PageIcon}}{{else}}fas fa-users{{end}} text-blue-600"
|
||||
></i>
|
||||
<span class="text-sm font-medium">Volunteer Management</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="p-6">
|
||||
<!-- Volunteer Info Section -->
|
||||
|
||||
@@ -1,27 +1,5 @@
|
||||
{{ define "content" }}
|
||||
<div class="flex-1 flex flex-col overflow-hidden">
|
||||
<!-- Top Navigation -->
|
||||
<div class="bg-white border-b border-gray-200 px-6 py-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<i
|
||||
class="{{if .PageIcon}}{{.PageIcon}}{{else}}fas fa-poll{{end}} text-green-600"
|
||||
></i>
|
||||
<span class="text-sm font-medium">Poll Questions</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<a
|
||||
href="/appointments"
|
||||
class="px-3 py-1 bg-gray-500 text-white text-sm hover:bg-gray-600 rounded"
|
||||
>
|
||||
Back to Appointments
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Form Content -->
|
||||
<div class="flex-1 overflow-y-auto bg-gray-50 p-6">
|
||||
<div class="max-w-2xl mx-auto">
|
||||
@@ -150,7 +128,7 @@
|
||||
<!-- Submit Button -->
|
||||
<div class="flex justify-end gap-3 pt-6">
|
||||
<a
|
||||
href="/appointments"
|
||||
href="/volunteer/Addresses"
|
||||
class="px-4 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-50"
|
||||
>
|
||||
Cancel
|
||||
|
||||
@@ -1,17 +1,5 @@
|
||||
{{ define "content" }}
|
||||
<div class="min-h-screen bg-gray-50">
|
||||
<!-- Top Navigation -->
|
||||
<div class="bg-white border-b border-gray-200 px-6 py-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
<i
|
||||
class="{{if .PageIcon}}{{.PageIcon}}{{else}}fas fa-users{{end}} text-blue-600"
|
||||
></i>
|
||||
<span class="text-sm font-medium">Volunteer Management</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="p-6 space-y-6">
|
||||
{{range .TeamLeads}} {{ $teamLeadID := .ID }}
|
||||
|
||||
@@ -1,20 +1,6 @@
|
||||
{{ define "content" }}
|
||||
<!-- Main Content -->
|
||||
<div class="flex-1 flex flex-col overflow-hidden" x-data="volunteerTable()">
|
||||
<!-- Top Navigation -->
|
||||
<div class="bg-white border-b border-gray-200 px-6 py-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<i
|
||||
class="{{if .PageIcon}}{{.PageIcon}}{{else}}fas fa-users{{end}} text-blue-600"
|
||||
></i>
|
||||
<span class="text-sm font-medium">Volunteer Management</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Toolbar -->
|
||||
<div class="bg-gray-50 border-b border-gray-200 px-6 py-3">
|
||||
<div class="flex items-center gap-4 text-sm">
|
||||
|
||||
BIN
app/tmp/main
BIN
app/tmp/main
Binary file not shown.
365
index.html
Normal file
365
index.html
Normal file
@@ -0,0 +1,365 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Moraine lake sunset - Google Search</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<style>
|
||||
@import url("https://fonts.googleapis.com/css2?family=Roboto:wght@400;500&display=swap");
|
||||
body {
|
||||
font-family: "Roboto", sans-serif;
|
||||
}
|
||||
.mountain-bg {
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
#4fc3f7 0%,
|
||||
#29b6f6 25%,
|
||||
#0277bd 50%,
|
||||
#01579b 75%,
|
||||
#263238 100%
|
||||
);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-white">
|
||||
<!-- Header with mountain background -->
|
||||
<div class="mountain-bg relative h-48 overflow-hidden">
|
||||
<!-- Mountain silhouettes -->
|
||||
<div class="absolute inset-0">
|
||||
<svg viewBox="0 0 1200 200" class="w-full h-full">
|
||||
<!-- Background mountains -->
|
||||
<polygon
|
||||
points="0,200 0,100 100,80 200,120 300,90 400,110 500,85 600,105 700,95 800,115 900,100 1000,120 1100,110 1200,130 1200,200"
|
||||
fill="rgba(69,90,100,0.4)"
|
||||
/>
|
||||
<!-- Mid mountains -->
|
||||
<polygon
|
||||
points="0,200 0,120 80,100 180,140 280,110 380,130 480,105 580,125 680,115 780,135 880,125 980,145 1080,135 1200,155 1200,200"
|
||||
fill="rgba(38,50,56,0.6)"
|
||||
/>
|
||||
<!-- Front mountains -->
|
||||
<polygon
|
||||
points="0,200 0,140 60,120 160,160 260,130 360,150 460,125 560,145 660,135 760,155 860,145 960,165 1060,155 1200,175 1200,200"
|
||||
fill="rgba(38,50,56,0.8)"
|
||||
/>
|
||||
<!-- Snow caps -->
|
||||
<polygon points="50,130 80,100 110,130" fill="white" opacity="0.9" />
|
||||
<polygon
|
||||
points="150,150 180,140 210,150"
|
||||
fill="white"
|
||||
opacity="0.9"
|
||||
/>
|
||||
<polygon
|
||||
points="250,140 280,110 310,140"
|
||||
fill="white"
|
||||
opacity="0.9"
|
||||
/>
|
||||
<polygon
|
||||
points="450,135 480,105 510,135"
|
||||
fill="white"
|
||||
opacity="0.9"
|
||||
/>
|
||||
<polygon
|
||||
points="650,145 680,115 710,145"
|
||||
fill="white"
|
||||
opacity="0.9"
|
||||
/>
|
||||
<polygon
|
||||
points="850,155 880,125 910,155"
|
||||
fill="white"
|
||||
opacity="0.9"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<!-- Google logo and search bar -->
|
||||
<div class="relative z-10 pt-8 px-4">
|
||||
<div class="max-w-2xl mx-auto">
|
||||
<!-- Google logo -->
|
||||
<div class="mb-8">
|
||||
<h1 class="text-white text-6xl font-normal tracking-tight">
|
||||
Google
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<!-- Search bar -->
|
||||
<div class="relative">
|
||||
<div
|
||||
class="bg-white rounded-full shadow-lg flex items-center px-4 py-3"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value="Moraine lake sunset"
|
||||
class="flex-1 text-gray-900 text-lg outline-none px-2"
|
||||
/>
|
||||
<button class="p-2 hover:bg-gray-100 rounded-full">
|
||||
<svg
|
||||
class="w-6 h-6 text-gray-400"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- User info -->
|
||||
<div class="absolute top-4 right-4 flex items-center space-x-4">
|
||||
<svg
|
||||
class="w-6 h-6 text-white opacity-70"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.532 1.532 0 01-2.286.948c-1.372-.836-2.942.734-2.106 2.106.54.886.061 2.042-.947 2.287-1.561.379-1.561 2.6 0 2.978a1.532 1.532 0 01.947 2.287c-.836 1.372.734 2.942 2.106 2.106a1.532 1.532 0 012.287.947c.379 1.561 2.6 1.561 2.978 0a1.533 1.533 0 012.287-.947c1.372.836 2.942-.734 2.106-2.106a1.533 1.533 0 01.947-2.287c1.561-.379 1.561-2.6 0-2.978a1.532 1.532 0 01-.947-2.287c.836-1.372-.734-2.942-2.106-2.106a1.532 1.532 0 01-2.287-.947zM10 13a3 3 0 100-6 3 3 0 000 6z"
|
||||
clip-rule="evenodd"
|
||||
></path>
|
||||
</svg>
|
||||
<div
|
||||
class="w-6 h-6 bg-white bg-opacity-20 rounded grid grid-cols-3 gap-0.5 p-1"
|
||||
>
|
||||
<div class="bg-white bg-opacity-60 rounded-sm"></div>
|
||||
<div class="bg-white bg-opacity-60 rounded-sm"></div>
|
||||
<div class="bg-white bg-opacity-60 rounded-sm"></div>
|
||||
<div class="bg-white bg-opacity-60 rounded-sm"></div>
|
||||
<div class="bg-white bg-opacity-60 rounded-sm"></div>
|
||||
<div class="bg-white bg-opacity-60 rounded-sm"></div>
|
||||
<div class="bg-white bg-opacity-60 rounded-sm"></div>
|
||||
<div class="bg-white bg-opacity-60 rounded-sm"></div>
|
||||
<div class="bg-white bg-opacity-60 rounded-sm"></div>
|
||||
</div>
|
||||
<span class="text-white text-sm">aurelien.salomon@gmail.com</span>
|
||||
<svg class="w-4 h-4 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
||||
clip-rule="evenodd"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Navigation -->
|
||||
<div class="bg-gray-700 border-b border-gray-600">
|
||||
<div class="max-w-6xl mx-auto px-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex space-x-8">
|
||||
<a
|
||||
href="#"
|
||||
class="text-blue-300 border-b-2 border-blue-300 py-3 px-1 text-sm font-medium"
|
||||
>WEB</a
|
||||
>
|
||||
<a href="#" class="text-gray-300 hover:text-white py-3 px-1 text-sm"
|
||||
>MAPS</a
|
||||
>
|
||||
<a href="#" class="text-gray-300 hover:text-white py-3 px-1 text-sm"
|
||||
>IMAGES</a
|
||||
>
|
||||
<a href="#" class="text-gray-300 hover:text-white py-3 px-1 text-sm"
|
||||
>VIDEOS</a
|
||||
>
|
||||
<div class="relative">
|
||||
<a
|
||||
href="#"
|
||||
class="text-gray-300 hover:text-white py-3 px-1 text-sm flex items-center"
|
||||
>
|
||||
MORE
|
||||
<svg
|
||||
class="w-4 h-4 ml-1"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
||||
clip-rule="evenodd"
|
||||
></path>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
<a href="#" class="text-gray-300 hover:text-white py-3 px-1 text-sm"
|
||||
>SEARCH TOOLS</a
|
||||
>
|
||||
</div>
|
||||
<div class="text-gray-400 text-sm">
|
||||
About 7,920,200 Results 0.18 seconds
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main content -->
|
||||
<div class="max-w-6xl mx-auto px-4 py-4 flex">
|
||||
<!-- Left column - Search results -->
|
||||
<div class="flex-1 max-w-2xl">
|
||||
<!-- Result 1 -->
|
||||
<div class="mb-6">
|
||||
<div class="text-sm text-green-600 mb-1">
|
||||
wikipedia.com/morainelake
|
||||
</div>
|
||||
<h3 class="text-xl text-blue-600 hover:underline cursor-pointer mb-2">
|
||||
Moraine lake - Wikipedia
|
||||
</h3>
|
||||
<p class="text-sm text-gray-600 leading-relaxed">
|
||||
Moraine Lake is a glacially-fed lake in Banff National Park, 14
|
||||
kilometres outside the Village of Lake Louise, Alberta, Canada. It
|
||||
is situated in the Valley of the Ten Peaks, at an elevation of
|
||||
approximately 6,183 feet. The lake has a surface area of 5 square
|
||||
kilometres. The lake, being glacially fed, does not reach its crest
|
||||
until mid to ...
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Result 2 -->
|
||||
<div class="mb-6">
|
||||
<div class="text-sm text-green-600 mb-1">rockies.com</div>
|
||||
<h3 class="text-xl text-blue-600 hover:underline cursor-pointer mb-2">
|
||||
Best places to photograph rockies
|
||||
</h3>
|
||||
<p class="text-sm text-gray-600 leading-relaxed">
|
||||
The light is usually best around sunrise and sunset, but interesting
|
||||
photographs. Moraine Lake is about a 20 minute drive from Lake
|
||||
Louise which is something ...
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Images section -->
|
||||
<div class="mb-8">
|
||||
<h3 class="text-xl text-gray-900 mb-4">
|
||||
Images for Moraine lake sunset
|
||||
</h3>
|
||||
<div class="grid grid-cols-4 gap-2">
|
||||
<div
|
||||
class="aspect-square bg-gradient-to-br from-blue-400 to-orange-400 rounded overflow-hidden"
|
||||
>
|
||||
<div
|
||||
class="w-full h-full bg-gradient-to-t from-blue-900 via-blue-400 to-orange-300 relative"
|
||||
>
|
||||
<div
|
||||
class="absolute bottom-0 w-full h-1/3 bg-gradient-to-r from-gray-800 to-gray-600"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="aspect-square bg-gradient-to-br from-orange-500 to-red-600 rounded overflow-hidden"
|
||||
>
|
||||
<div
|
||||
class="w-full h-full bg-gradient-to-t from-gray-900 via-orange-500 to-yellow-400 relative"
|
||||
>
|
||||
<div
|
||||
class="absolute bottom-0 w-full h-1/3 bg-gradient-to-r from-gray-900 to-gray-700"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="aspect-square bg-gradient-to-br from-yellow-400 to-orange-500 rounded overflow-hidden"
|
||||
>
|
||||
<div
|
||||
class="w-full h-full bg-gradient-to-t from-blue-800 via-yellow-400 to-orange-300 relative"
|
||||
>
|
||||
<div
|
||||
class="absolute bottom-0 w-full h-1/3 bg-gradient-to-r from-gray-800 to-gray-600"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="aspect-square bg-gradient-to-br from-green-400 to-blue-500 rounded overflow-hidden"
|
||||
>
|
||||
<div
|
||||
class="w-full h-full bg-gradient-to-t from-green-800 via-blue-400 to-orange-200 relative"
|
||||
>
|
||||
<div
|
||||
class="absolute bottom-0 w-full h-1/3 bg-gradient-to-r from-gray-900 to-gray-700"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Result 3 -->
|
||||
<div class="mb-6">
|
||||
<div class="text-sm text-green-600 mb-1">estravel.com</div>
|
||||
<h3 class="text-xl text-blue-600 hover:underline cursor-pointer mb-2">
|
||||
Best point for sunset in Alberta
|
||||
</h3>
|
||||
<p class="text-sm text-gray-600 leading-relaxed">
|
||||
Moraine Lake is only half the size of its nearby neighbour Lake
|
||||
Louise, but perhaps even more scenic. It's a glacier-fed lake
|
||||
situated in the beautiful valley of the ten ...
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Result 4 -->
|
||||
<div class="mb-6">
|
||||
<div class="text-sm text-green-600 mb-1">sunset.com</div>
|
||||
<h3 class="text-xl text-blue-600 hover:underline cursor-pointer mb-2">
|
||||
Sunset in the canada rocks
|
||||
</h3>
|
||||
<p class="text-sm text-gray-600 leading-relaxed">
|
||||
Moraine Lake is only half the size of its nearby neighbour Lake
|
||||
Louise, but perhaps even more scenic. It's a glacier-fed lake
|
||||
situated in the beautiful valley of the ten ...
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right column - Weather widget -->
|
||||
<div class="ml-8 w-80">
|
||||
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-4">
|
||||
<div class="mb-4">
|
||||
<h3 class="text-lg font-medium text-gray-900">Moraine lake</h3>
|
||||
<p class="text-sm text-gray-500">Sunshine</p>
|
||||
</div>
|
||||
|
||||
<!-- Weather chart -->
|
||||
<div class="mb-4">
|
||||
<div
|
||||
class="relative h-32 bg-gradient-to-t from-yellow-300 via-yellow-400 to-blue-400 rounded-lg overflow-hidden"
|
||||
>
|
||||
<!-- Sun -->
|
||||
<div
|
||||
class="absolute top-4 right-8 w-8 h-8 bg-yellow-400 rounded-full border-4 border-yellow-300"
|
||||
></div>
|
||||
|
||||
<!-- Time indicator -->
|
||||
<div
|
||||
class="absolute top-2 right-2 text-xs font-medium text-gray-700"
|
||||
>
|
||||
6:35 PM<br />
|
||||
<span class="text-blue-600">Sunset</span>
|
||||
</div>
|
||||
|
||||
<!-- Time markers -->
|
||||
<div
|
||||
class="absolute bottom-2 left-0 right-0 flex justify-between text-xs text-gray-600 px-2"
|
||||
>
|
||||
<span>6:00 AM</span>
|
||||
<span>1:00 PM</span>
|
||||
<span>8:00 PM</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Days -->
|
||||
<div class="grid grid-cols-4 gap-2 text-center text-sm">
|
||||
<div class="text-gray-900 font-medium">TODAY</div>
|
||||
<div class="text-gray-500">THURSDAY</div>
|
||||
<div class="text-gray-500">FRIDAY</div>
|
||||
<div class="text-gray-500">SATURDAY</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user