package main import ( "errors" "net/http" "strconv" ) // handleKioskGet returns the volunteer's profile, current shift assignments, and // available open shifts in their department. Authenticated by volunteer token only — // no JWT required. func (app *App) handleKioskGet(w http.ResponseWriter, r *http.Request) { token := r.PathValue("token") a, err := app.getAttendeeByToken(token) if err != nil || a == nil { writeError(w, "not found", http.StatusNotFound) return } v, _ := app.getVolunteerByAttendeeID(a.ID) if v == nil { writeError(w, "no volunteer record linked to this token", http.StatusNotFound) return } assigned, _ := app.listShiftsForVolunteer(v.ID) if assigned == nil { assigned = []Shift{} } var available []Shift if v.DepartmentID != nil { available, _ = app.listOpenShiftsForDept(*v.DepartmentID) } if available == nil { available = []Shift{} } writeJSON(w, map[string]any{ "volunteer": v, "shifts": assigned, "available": available, }) } // handleKioskClaim assigns the volunteer to a shift. // Without ?force=true it returns 409 with conflicting shifts on overlap. func (app *App) handleKioskClaim(w http.ResponseWriter, r *http.Request) { token := r.PathValue("token") shiftID, err := strconv.Atoi(r.PathValue("id")) if err != nil { writeError(w, "invalid shift id", http.StatusBadRequest) return } a, err := app.getAttendeeByToken(token) if err != nil || a == nil { writeError(w, "not found", http.StatusNotFound) return } v, _ := app.getVolunteerByAttendeeID(a.ID) if v == nil { writeError(w, "no volunteer linked to this token", http.StatusNotFound) return } force := r.URL.Query().Get("force") == "true" if !force { conflicts, err := app.checkShiftConflict(v.ID, shiftID) if err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } if len(conflicts) > 0 { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusConflict) writeJSON(w, map[string]any{ "conflict": true, "conflicting_shifts": conflicts, }) return } } shift, err := app.getShift(shiftID) if err != nil || shift == nil { writeError(w, "shift not found", http.StatusNotFound) return } if err := app.assignShiftWithCapacity(v.ID, shiftID, shift.Capacity); err != nil { if errors.Is(err, errShiftFull) { writeError(w, "shift is full", http.StatusConflict) return } writeError(w, err.Error(), http.StatusInternalServerError) return } app.handleKioskGet(w, r) } // handleKioskUnclaim removes the volunteer from a shift. func (app *App) handleKioskUnclaim(w http.ResponseWriter, r *http.Request) { token := r.PathValue("token") shiftID, err := strconv.Atoi(r.PathValue("id")) if err != nil { writeError(w, "invalid shift id", http.StatusBadRequest) return } a, err := app.getAttendeeByToken(token) if err != nil || a == nil { writeError(w, "not found", http.StatusNotFound) return } v, _ := app.getVolunteerByAttendeeID(a.ID) if v == nil { writeError(w, "no volunteer linked to this token", http.StatusNotFound) return } if err := app.unassignShift(v.ID, shiftID); err != nil { writeError(w, err.Error(), http.StatusInternalServerError) return } app.handleKioskGet(w, r) }