top bar update
This commit is contained in:
8
.gitignore
vendored
8
.gitignore
vendored
@@ -1,4 +1,4 @@
|
|||||||
/uploads
|
/app/uploads
|
||||||
.env
|
/app/.env
|
||||||
/tmp
|
/app/tmp/
|
||||||
/Example_code
|
/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
|
// Set button properties based on poll response status
|
||||||
a.HasPollResponse = pollResponseExists
|
a.HasPollResponse = pollResponseExists
|
||||||
if pollResponseExists {
|
if pollResponseExists {
|
||||||
a.PollButtonText = "Poll Taken"
|
a.PollButtonText = "Poll"
|
||||||
a.PollButtonClass = "px-3 py-1 bg-green-600 text-white text-sm rounded cursor-not-allowed"
|
a.PollButtonClass = "px-1 py-1 bg-green-600 text-white text-sm rounded cursor-not-allowed"
|
||||||
} else {
|
} else {
|
||||||
a.PollButtonText = "Ask Poll"
|
a.PollButtonText = "Ask"
|
||||||
a.PollButtonClass = "px-3 py-1 bg-blue-600 text-white text-sm hover:bg-blue-700 rounded"
|
a.PollButtonClass = "px-1 py-1 bg-blue-600 text-white text-sm hover:bg-blue-700 rounded"
|
||||||
}
|
}
|
||||||
|
|
||||||
appointments = append(appointments, a)
|
appointments = append(appointments, a)
|
||||||
|
|||||||
@@ -150,7 +150,6 @@ func getVolunteerStatistics(userID int) (*VolunteerStatistics, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
fmt.Print("Stats: ", stats.AppointmentsThisWeek," Today's date: " , today ,"fasd", weekStart)
|
|
||||||
|
|
||||||
|
|
||||||
// Total appointments
|
// Total appointments
|
||||||
|
|||||||
@@ -1,25 +1,5 @@
|
|||||||
{{ define "content" }}
|
{{ define "content" }}
|
||||||
<div class="flex-1 flex flex-col overflow-hidden">
|
<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 -->
|
<!-- Toolbar -->
|
||||||
<div class="bg-gray-50 border-b border-gray-200 px-6 py-3">
|
<div class="bg-gray-50 border-b border-gray-200 px-6 py-3">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
|
|||||||
@@ -1,19 +1,5 @@
|
|||||||
{{ define "content" }}
|
{{ define "content" }}
|
||||||
<div class="flex-1 flex flex-col overflow-hidden">
|
<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 -->
|
<!-- Toolbar -->
|
||||||
<div class="bg-gray-50 border-b border-gray-200 px-6 py-3">
|
<div class="bg-gray-50 border-b border-gray-200 px-6 py-3">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
@@ -41,16 +27,28 @@
|
|||||||
<tr
|
<tr
|
||||||
class="text-left text-gray-700 font-medium border-b border-gray-200"
|
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">Address</th>
|
||||||
<th class="px-6 py-3 whitespace-nowrap">Coordinates</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 Date</th>
|
||||||
<th class="px-6 py-3 whitespace-nowrap">Appointment Time</th>
|
<th class="px-6 py-3 whitespace-nowrap">Appointment Time</th>
|
||||||
<th class="px-6 py-3 whitespace-nowrap">Poll Question</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="divide-y divide-gray-200">
|
<tbody class="divide-y divide-gray-200">
|
||||||
{{ range .Appointments }}
|
{{ range .Appointments }}
|
||||||
<tr class="hover:bg-gray-50">
|
<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">{{ .Address }}</td>
|
||||||
<td class="px-6 py-3 whitespace-nowrap">
|
<td class="px-6 py-3 whitespace-nowrap">
|
||||||
<a
|
<a
|
||||||
@@ -67,18 +65,6 @@
|
|||||||
<td class="px-6 py-3 whitespace-nowrap">
|
<td class="px-6 py-3 whitespace-nowrap">
|
||||||
{{ .AppointmentTime.Format "15:04" }}
|
{{ .AppointmentTime.Format "15:04" }}
|
||||||
</td>
|
</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>
|
</tr>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -18,40 +18,6 @@
|
|||||||
<body class="bg-gray-50">
|
<body class="bg-gray-50">
|
||||||
<!-- Full Width Container -->
|
<!-- Full Width Container -->
|
||||||
<div class="min-h-screen w-full flex flex-col">
|
<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 -->
|
<!-- Main Dashboard Content -->
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<!-- Stats Grid - Full Width -->
|
<!-- Stats Grid - Full Width -->
|
||||||
|
|||||||
@@ -1,29 +1,18 @@
|
|||||||
{{ define "content" }}
|
{{ define "content" }}
|
||||||
<div class="flex-1 flex flex-col overflow-hidden">
|
<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 -->
|
<!-- Main Content -->
|
||||||
<div class="flex-1 overflow-hidden bg-gray-50">
|
<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 -->
|
<!-- 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}}
|
{{ if .Posts }}{{range .Posts}}
|
||||||
|
<!-- Posts Feed -->
|
||||||
<article class="bg-white border-b border-gray-200">
|
<article class="bg-white border-b border-gray-200">
|
||||||
<!-- Post Header -->
|
<!-- 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="flex-shrink-0">
|
||||||
<div
|
<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}}
|
{{slice .AuthorName 0 1}}
|
||||||
</div>
|
</div>
|
||||||
@@ -50,7 +39,7 @@
|
|||||||
|
|
||||||
<!-- Post Content -->
|
<!-- Post Content -->
|
||||||
{{if .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">
|
<p class="text-gray-900 leading-relaxed">
|
||||||
<span class="font-semibold">{{.AuthorName}}</span> {{.Content}}
|
<span class="font-semibold">{{.AuthorName}}</span> {{.Content}}
|
||||||
</p>
|
</p>
|
||||||
@@ -58,7 +47,7 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
</article>
|
</article>
|
||||||
{{else}}
|
{{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">
|
<div class="max-w-sm mx-auto">
|
||||||
<svg
|
<svg
|
||||||
class="w-16 h-16 mx-auto text-gray-300 mb-4"
|
class="w-16 h-16 mx-auto text-gray-300 mb-4"
|
||||||
@@ -74,6 +63,9 @@
|
|||||||
></path>
|
></path>
|
||||||
</svg>
|
</svg>
|
||||||
<h3 class="text-lg font-medium text-gray-900 mb-2">No posts yet</h3>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
{{ end }} {{ else }}
|
{{ end }} {{ else }}
|
||||||
@@ -94,7 +86,163 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Right Column - Statistics -->
|
<!-- 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 -->
|
<!-- Today's Overview -->
|
||||||
<div class="bg-white border-b border-gray-200">
|
<div class="bg-white border-b border-gray-200">
|
||||||
<div class="px-4 sm:px-6 py-4">
|
<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"
|
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"
|
||||||
/>
|
/>
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-white font-sans">
|
<body class="bg-gray-50 font-sans">
|
||||||
{{ if .IsAuthenticated }}
|
{{ if .IsAuthenticated }}
|
||||||
<!-- Authenticated User Interface -->
|
<!-- Authenticated User Interface -->
|
||||||
<div class="w-full h-screen bg-white overflow-hidden" x-data="{ sidebarOpen: false }">
|
<div class="min-h-screen" x-data="{ mobileMenuOpen: false }">
|
||||||
<!-- Mobile Header -->
|
|
||||||
<div class="lg:hidden bg-gray-100 px-4 py-3 flex items-center justify-between border-b border-gray-200">
|
<!-- Simple Navigation Bar -->
|
||||||
<div class="flex items-center gap-2">
|
<nav class="bg-gray-700 border-b border-gray-600 fixed top-0 left-0 w-full z-50">
|
||||||
<div class="w-5 h-5 bg-orange-500 rounded text-white text-xs flex items-center justify-center font-bold">
|
<div class="max-w-9xl mx-auto px-4 sm:px-6 lg:px-8 ">
|
||||||
L
|
<div class="flex justify-between items-center h-14 ">
|
||||||
</div>
|
|
||||||
<span class="text-sm font-medium">Poll System</span>
|
<!-- Left: Logo and Navigation Links -->
|
||||||
</div>
|
<div class="flex items-center space-x-12">
|
||||||
<div class="flex items-center gap-2">
|
<!-- Logo -->
|
||||||
<button @click="sidebarOpen = !sidebarOpen" class="p-2 hover:bg-gray-200 rounded">
|
<div class="flex items-center space-x-4">
|
||||||
<i class="fas fa-bars text-gray-600"></i>
|
<img src="../../static/icon-512.png" alt="Logo" class="w-6 h-6"/>
|
||||||
</button>
|
<span class="text-xl font-semibold text-white">Poll System</span>
|
||||||
<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>
|
|
||||||
|
|
||||||
<!-- 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>
|
|
||||||
|
|
||||||
<!-- 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
|
|
||||||
</div>
|
</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>
|
</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>
|
||||||
|
|
||||||
<div class="p-3 space-y-4">
|
<!-- Mobile Navigation Menu -->
|
||||||
<div class="space-y-1">
|
<div x-show="mobileMenuOpen"
|
||||||
{{ if .ShowAdminNav }}
|
x-transition:enter="transition ease-out duration-200"
|
||||||
<a href="/dashboard"
|
x-transition:enter-start="opacity-0 scale-95"
|
||||||
@click="sidebarOpen = false"
|
x-transition:enter-end="opacity-100 scale-100"
|
||||||
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}}">
|
x-transition:leave="transition ease-in duration-150"
|
||||||
<i class="fas fa-chart-pie text-gray-400 mr-2"></i>
|
x-transition:leave-start="opacity-100 scale-100"
|
||||||
<span>Dashboard</span>
|
x-transition:leave-end="opacity-0 scale-95"
|
||||||
</a>
|
class="md:hidden border-t border-gray-600"
|
||||||
<a href="/volunteers"
|
@click.outside="mobileMenuOpen = false">
|
||||||
@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}}">
|
<div class="px-4 py-4 bg-gray-800">
|
||||||
<i class="fas fa-users text-gray-400 mr-2"></i>
|
<!-- User Info Mobile -->
|
||||||
<span>Volunteers</span>
|
<div class="flex items-center space-x-3 pb-4 mb-4 border-b border-gray-600">
|
||||||
</a>
|
<div class="w-10 h-10 bg-blue-500 flex items-center justify-center text-white font-medium rounded-full">
|
||||||
<a href="/team_builder"
|
{{slice .UserName 0 1}}
|
||||||
@click="sidebarOpen = false"
|
</div>
|
||||||
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}}">
|
<div>
|
||||||
<i class="fas fa-user-friends text-gray-400 mr-2"></i>
|
<p class="text-sm font-medium text-white">{{.UserName}}</p>
|
||||||
<span>Team Builder</span>
|
<p class="text-xs text-gray-400">Logged in</p>
|
||||||
</a>
|
</div>
|
||||||
<a href="/addresses"
|
</div>
|
||||||
@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 }}
|
|
||||||
|
|
||||||
{{ if .ShowVolunteerNav }}
|
<!-- Mobile Navigation Links -->
|
||||||
<a href="/volunteer/dashboard"
|
<div class="space-y-2">
|
||||||
@click="sidebarOpen = false"
|
{{ if .ShowAdminNav }}
|
||||||
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}}">
|
<a href="/dashboard" @click="mobileMenuOpen = false"
|
||||||
<i class="fas fa-chart-pie text-gray-400 mr-2"></i>
|
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}}">
|
||||||
<span>Dashboard</span>
|
<i class="fas fa-chart-pie mr-3 w-4"></i>Dashboard
|
||||||
</a>
|
</a>
|
||||||
<a href="/volunteer/Addresses"
|
<a href="/volunteers" @click="mobileMenuOpen = false"
|
||||||
@click="sidebarOpen = 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}}">
|
||||||
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-users mr-3 w-4"></i>Volunteers
|
||||||
<i class="fas fa-home text-gray-400 mr-2"></i>
|
</a>
|
||||||
<span>Assigned Address</span>
|
<a href="/team_builder" @click="mobileMenuOpen = false"
|
||||||
</a>
|
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}}">
|
||||||
<a href="/volunteer/schedual"
|
<i class="fas fa-user-friends mr-3 w-4"></i>Team Builder
|
||||||
@click="sidebarOpen = false"
|
</a>
|
||||||
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}}">
|
<a href="/addresses" @click="mobileMenuOpen = false"
|
||||||
<i class="fas fa-calendar-alt text-gray-400 mr-2"></i>
|
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}}">
|
||||||
<span>My Schedule</span>
|
<i class="fas fa-map-marked-alt mr-3 w-4"></i>Addresses
|
||||||
</a>
|
</a>
|
||||||
{{ end }}
|
<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 }}
|
||||||
|
|
||||||
<a href="/profile"
|
{{ if .ShowVolunteerNav }}
|
||||||
@click="sidebarOpen = false"
|
<a href="/volunteer/dashboard" @click="mobileMenuOpen = 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}}">
|
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-user-circle text-gray-400 mr-2"></i>
|
<i class="fas fa-chart-pie mr-3 w-4"></i>Dashboard
|
||||||
<span>Profile</span>
|
</a>
|
||||||
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
<!-- Main Content -->
|
<!-- Main Content Area -->
|
||||||
<div class="flex-1 flex flex-col overflow-hidden min-h-screen">
|
<main class="flex-1 mt-14">
|
||||||
<div class="bg-white flex-1 overflow-auto pb-[60px]">
|
<!--sm:px-4 lg:px-6-->
|
||||||
{{ template "content" . }}
|
<div class="max-w-9xl mx-auto overflow-hidden ">
|
||||||
</div>
|
{{ template "content" . }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{else}}
|
{{else}}
|
||||||
<!-- Landing Page -->
|
<!-- Landing Page (unchanged) -->
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
@@ -193,63 +206,63 @@
|
|||||||
<body class="bg-gray-50">
|
<body class="bg-gray-50">
|
||||||
<div class="min-h-screen" x-data="{ mobileMenuOpen: false }">
|
<div class="min-h-screen" x-data="{ mobileMenuOpen: false }">
|
||||||
|
|
||||||
<!-- Navigation -->
|
<!-- 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="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 -->
|
<!-- Logo -->
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<img src="../../static/icon-512.png" alt="Logo" class="w-8 h-8"/>
|
<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>
|
</div>
|
||||||
|
|
||||||
<!-- Desktop Navigation -->
|
<!-- Desktop Navigation -->
|
||||||
<div class="hidden md:flex items-center gap-8">
|
<div class="hidden md:flex items-center gap-8">
|
||||||
<a href="#home" class="text-gray-600 hover:text-gray-900">Home</a>
|
<a href="#home" class="text-gray-300 hover:text-white py-3 px-1">Home</a>
|
||||||
<a href="#products" class="text-gray-600 hover:text-gray-900">Products</a>
|
<a href="#products" class="text-gray-300 hover:text-white py-3 px-1">Products</a>
|
||||||
<a href="#about" class="text-gray-600 hover:text-gray-900">About</a>
|
<a href="#about" class="text-gray-300 hover:text-white py-3 px-1">About</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Auth Buttons -->
|
<!-- Auth Buttons -->
|
||||||
<div class="hidden md:flex items-center gap-3">
|
<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
|
Sign In
|
||||||
</button>
|
</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
|
Get Started
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Mobile Menu Button -->
|
<!-- Mobile Menu Button -->
|
||||||
<button @click="mobileMenuOpen = !mobileMenuOpen" class="md:hidden p-2">
|
<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 text-gray-600" x-show="!mobileMenuOpen"></i>
|
<i class="fas fa-bars" x-show="!mobileMenuOpen"></i>
|
||||||
<i class="fas fa-times text-gray-600" x-show="mobileMenuOpen"></i>
|
<i class="fas fa-times" x-show="mobileMenuOpen"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Mobile Menu -->
|
<!-- 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">
|
<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="#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-600">Products</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-600">About</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-200 pt-3 space-y-2">
|
<div class="border-t border-gray-600 pt-3 space-y-2">
|
||||||
<button onclick="openLoginModal(); document.querySelector('[x-data]').__x.$data.mobileMenuOpen = false"
|
<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
|
Sign In
|
||||||
</button>
|
</button>
|
||||||
<button onclick="openRegisterModal(); document.querySelector('[x-data]').__x.$data.mobileMenuOpen = false"
|
<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
|
Get Started
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<!-- Hero -->
|
<!-- 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">
|
<div class="max-w-4xl mx-auto text-center">
|
||||||
<h1 class="text-4xl sm:text-5xl font-bold text-gray-900 mb-6">
|
<h1 class="text-4xl sm:text-5xl font-bold text-gray-900 mb-6">
|
||||||
Simple Polling
|
Simple Polling
|
||||||
@@ -272,7 +285,7 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- Products 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="max-w-6xl mx-auto px-6">
|
||||||
<div class="text-center mb-12">
|
<div class="text-center mb-12">
|
||||||
<h2 class="text-3xl font-bold text-gray-900 mb-4">Our Products</h2>
|
<h2 class="text-3xl font-bold text-gray-900 mb-4">Our Products</h2>
|
||||||
@@ -317,7 +330,7 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- About -->
|
<!-- 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="max-w-6xl mx-auto px-6">
|
||||||
<div class="grid lg:grid-cols-2 gap-12 items-center">
|
<div class="grid lg:grid-cols-2 gap-12 items-center">
|
||||||
<div>
|
<div>
|
||||||
@@ -380,13 +393,7 @@
|
|||||||
<div class="flex flex-col lg:flex-row min-h-[500px]">
|
<div class="flex flex-col lg:flex-row min-h-[500px]">
|
||||||
<!-- Left Side - Image -->
|
<!-- 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="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">
|
<img src="../../static/feature-mobile2.jpg" alt="Welcome Image" class="object-cover h-full rounded-lg shadow-lg">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<!-- Right Side - Form -->
|
<!-- Right Side - Form -->
|
||||||
<div class="flex-1 p-6 sm:p-8">
|
<div class="flex-1 p-6 sm:p-8">
|
||||||
@@ -426,11 +433,6 @@
|
|||||||
<div class="flex flex-col lg:flex-row min-h-[600px]">
|
<div class="flex flex-col lg:flex-row min-h-[600px]">
|
||||||
<!-- Left Side - Image -->
|
<!-- 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="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">
|
<img src="../../static/feature-mobile1.jpg" alt="Welcome Image" class="object-cover h-full rounded-lg shadow-lg">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,6 @@
|
|||||||
{{ define "content" }}
|
{{ define "content" }}
|
||||||
<div class="min-h-screen bg-gray-100">
|
<div class="min-h-screen bg-gray-100">
|
||||||
<!-- Header -->
|
<!-- 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">
|
<div class="max-w-2xl mx-auto">
|
||||||
<!-- Create Post Form -->
|
<!-- Create Post Form -->
|
||||||
|
|||||||
@@ -1,18 +1,5 @@
|
|||||||
{{ define "content" }}
|
{{ define "content" }}
|
||||||
<div class="min-h-screen bg-gray-50">
|
<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 -->
|
<!-- Main Content -->
|
||||||
<div class="p-6">
|
<div class="p-6">
|
||||||
<!-- Profile Info Section -->
|
<!-- Profile Info Section -->
|
||||||
|
|||||||
@@ -17,30 +17,6 @@
|
|||||||
</head>
|
</head>
|
||||||
<body class="bg-gray-100">
|
<body class="bg-gray-100">
|
||||||
<div class="flex-1 flex flex-col overflow-hidden">
|
<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 -->
|
<!-- Dashboard Content -->
|
||||||
<div class="flex-1 overflow-auto">
|
<div class="flex-1 overflow-auto">
|
||||||
<!-- Top Stats Cards -->
|
<!-- Top Stats Cards -->
|
||||||
|
|||||||
@@ -2,20 +2,6 @@
|
|||||||
<div class="min-h-screen bg-gray-50">
|
<div class="min-h-screen bg-gray-50">
|
||||||
<!-- Header Bar -->
|
<!-- 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 -->
|
<!-- Main Content -->
|
||||||
<div class="p-6">
|
<div class="p-6">
|
||||||
<!-- Volunteer Info Section -->
|
<!-- Volunteer Info Section -->
|
||||||
|
|||||||
@@ -1,27 +1,5 @@
|
|||||||
{{ define "content" }}
|
{{ define "content" }}
|
||||||
<div class="flex-1 flex flex-col overflow-hidden">
|
<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 -->
|
<!-- Form Content -->
|
||||||
<div class="flex-1 overflow-y-auto bg-gray-50 p-6">
|
<div class="flex-1 overflow-y-auto bg-gray-50 p-6">
|
||||||
<div class="max-w-2xl mx-auto">
|
<div class="max-w-2xl mx-auto">
|
||||||
@@ -150,7 +128,7 @@
|
|||||||
<!-- Submit Button -->
|
<!-- Submit Button -->
|
||||||
<div class="flex justify-end gap-3 pt-6">
|
<div class="flex justify-end gap-3 pt-6">
|
||||||
<a
|
<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"
|
class="px-4 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-50"
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
|
|||||||
@@ -1,17 +1,5 @@
|
|||||||
{{ define "content" }}
|
{{ define "content" }}
|
||||||
<div class="min-h-screen bg-gray-50">
|
<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 -->
|
<!-- Main Content -->
|
||||||
<div class="p-6 space-y-6">
|
<div class="p-6 space-y-6">
|
||||||
{{range .TeamLeads}} {{ $teamLeadID := .ID }}
|
{{range .TeamLeads}} {{ $teamLeadID := .ID }}
|
||||||
|
|||||||
@@ -1,20 +1,6 @@
|
|||||||
{{ define "content" }}
|
{{ define "content" }}
|
||||||
<!-- Main Content -->
|
<!-- Main Content -->
|
||||||
<div class="flex-1 flex flex-col overflow-hidden" x-data="volunteerTable()">
|
<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 -->
|
<!-- Toolbar -->
|
||||||
<div class="bg-gray-50 border-b border-gray-200 px-6 py-3">
|
<div class="bg-gray-50 border-b border-gray-200 px-6 py-3">
|
||||||
<div class="flex items-center gap-4 text-sm">
|
<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