Turnpike/handle_attendees.go

168 lines
4.5 KiB
Go
Raw Normal View History

package main
import (
"encoding/csv"
"encoding/json"
"net/http"
"strconv"
)
func (app *App) handleListAttendees(w http.ResponseWriter, r *http.Request) {
q := r.URL.Query()
attendees, err := app.listAttendees(q.Get("search"), q.Get("ticket_type"), q.Get("checked_in"))
if err != nil {
writeError(w, err.Error(), http.StatusInternalServerError)
return
}
types, _ := app.attendeeTicketTypes()
total, checkedIn, _ := app.attendeeCounts()
writeJSON(w, map[string]any{
"attendees": attendees,
"ticket_types": types,
"total": total,
"checked_in": checkedIn,
})
}
func (app *App) handleCreateAttendee(w http.ResponseWriter, r *http.Request) {
var a Attendee
if err := json.NewDecoder(r.Body).Decode(&a); err != nil {
writeError(w, "invalid request", http.StatusBadRequest)
return
}
if a.Name == "" {
writeError(w, "name is required", http.StatusBadRequest)
return
}
created, err := app.createAttendee(a)
if err != nil {
writeError(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusCreated)
writeJSON(w, created)
}
func (app *App) handleGetAttendee(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(r.PathValue("id"))
if err != nil {
writeError(w, "invalid id", http.StatusBadRequest)
return
}
a, err := app.getAttendee(id)
if err != nil || a == nil {
writeError(w, "not found", http.StatusNotFound)
return
}
writeJSON(w, a)
}
func (app *App) handleUpdateAttendee(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(r.PathValue("id"))
if err != nil {
writeError(w, "invalid id", http.StatusBadRequest)
return
}
var a Attendee
if err := json.NewDecoder(r.Body).Decode(&a); err != nil {
writeError(w, "invalid request", http.StatusBadRequest)
return
}
if a.Name == "" {
writeError(w, "name is required", http.StatusBadRequest)
return
}
a.ID = id
if err := app.updateAttendee(a); err != nil {
writeError(w, err.Error(), http.StatusInternalServerError)
return
}
updated, _ := app.getAttendee(id)
writeJSON(w, updated)
}
func (app *App) handleDeleteAttendee(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(r.PathValue("id"))
if err != nil {
writeError(w, "invalid id", http.StatusBadRequest)
return
}
if err := app.deleteAttendee(id); err != nil {
writeError(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusNoContent)
}
// handleCheckInAttendee handles POST /api/attendees/:id/checkin.
// Optional body: {"count": N, "also_volunteer": true}
// Returns {"attendee": ..., "volunteer": ...} — volunteer is included if also_volunteer=true
// and the attendee has a linked volunteer record.
func (app *App) handleCheckInAttendee(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(r.PathValue("id"))
if err != nil {
writeError(w, "invalid id", http.StatusBadRequest)
return
}
var body struct {
Count int `json:"count"`
AlsoVolunteer bool `json:"also_volunteer"`
}
body.Count = 1
json.NewDecoder(r.Body).Decode(&body)
if body.Count < 1 {
body.Count = 1
}
claims := claimsFromContext(r)
a, err := app.checkInAttendee(id, claims.UserID, body.Count)
if err != nil {
writeError(w, err.Error(), http.StatusInternalServerError)
return
}
result := map[string]any{"attendee": a}
if body.AlsoVolunteer {
v, _ := app.getVolunteerByAttendeeID(id)
if v != nil {
if !v.CheckedIn {
if v2, err := app.checkInVolunteer(v.ID, claims.UserID); err == nil {
result["volunteer"] = v2
app.broker.publish("checkin", map[string]any{"type": "volunteer", "volunteer": v2})
}
} else {
result["volunteer"] = v
}
}
}
app.broker.publish("checkin", map[string]any{"type": "attendee", "attendee": a})
writeJSON(w, result)
}
func (app *App) handleExportAttendees(w http.ResponseWriter, r *http.Request) {
attendees, err := app.listAttendees("", "", "")
if err != nil {
writeError(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "text/csv")
w.Header().Set("Content-Disposition", `attachment; filename="attendees.csv"`)
wr := csv.NewWriter(w)
wr.Write([]string{"name", "email", "phone", "ticket_id", "ticket_type", "party_size", "checked_in_count", "note", "checked_in"})
for _, a := range attendees {
ci := "no"
if a.CheckedIn {
ci = "yes"
}
wr.Write([]string{
a.Name, a.Email, a.Phone, a.TicketID, a.TicketType,
strconv.Itoa(a.PartySize), strconv.Itoa(a.CheckedInCount),
a.Note, ci,
})
}
wr.Flush()
}