aboutsummaryrefslogtreecommitdiff
path: root/web/categories.go
diff options
context:
space:
mode:
authorMartin Kagamino Lehoux <martin@lehoux.net>2026-08-23 21:35:59 +0200
committerMartin Kagamino Lehoux <martin@lehoux.net>2026-08-23 21:35:59 +0200
commit08fdd6964fd9ef29356420b035e2d186546f674a (patch)
tree812565b5ca57405a5b36b2d7edfab983f6f079d4 /web/categories.go
parent2d8503cafb9f63ca4cb63f041281987f9a63f16a (diff)
Add hardcore ride categorisation with bike and ride types
Diffstat (limited to 'web/categories.go')
-rw-r--r--web/categories.go89
1 files changed, 89 insertions, 0 deletions
diff --git a/web/categories.go b/web/categories.go
new file mode 100644
index 0000000..4077092
--- /dev/null
+++ b/web/categories.go
@@ -0,0 +1,89 @@
+package web
+
+import (
+ "log/slog"
+ "net/http"
+ "strconv"
+
+ "github.com/martinlehoux/biking_home/ride"
+)
+
+func (s *Server) handleUpdateRideCategories(w http.ResponseWriter, r *http.Request) {
+ id, err := strconv.ParseInt(r.PathValue("id"), 10, 64)
+ if err != nil || id <= 0 {
+ http.NotFound(w, r)
+ return
+ }
+ item, found, err := ride.GetByID(s.db, id)
+ if err != nil {
+ slog.Error("Failed to load ride for category update", "ride_id", id, "error", err)
+ http.Error(w, "failed to load ride", http.StatusInternalServerError)
+ return
+ }
+ if !found {
+ http.NotFound(w, r)
+ return
+ }
+ _ = item
+ if err := r.ParseForm(); err != nil {
+ http.Error(w, "invalid form", http.StatusBadRequest)
+ return
+ }
+ bikeType := ride.BikeType(r.FormValue("bike_type"))
+ rideType := ride.RideType(r.FormValue("ride_type"))
+ if !ride.IsValidBikeType(string(bikeType)) {
+ http.Error(w, "invalid bike_type", http.StatusBadRequest)
+ return
+ }
+ if !ride.IsValidRideType(string(rideType)) {
+ http.Error(w, "invalid ride_type", http.StatusBadRequest)
+ return
+ }
+ if err := ride.UpdateCategories(s.db, id, bikeType, rideType); err != nil {
+ slog.Error("Failed to update ride categories", "ride_id", id, "error", err)
+ http.Error(w, "failed to update categories", http.StatusInternalServerError)
+ return
+ }
+ slog.Info("Updated ride categories", "ride_id", id, "bike_type", bikeType, "ride_type", rideType)
+ http.Redirect(w, r, rideDetailURL(id)+"?categories=updated", http.StatusSeeOther)
+}
+
+func rideCategoriesUpdateURL(id int64) string {
+ return rideDetailURL(id) + "/categories"
+}
+
+var bikeTypeOptions = []ride.BikeType{ride.BikeTypeRoad, ride.BikeTypeMTB}
+var rideTypeOptions = []ride.RideType{ride.RideTypeTraining, ride.RideTypeIndoor, ride.RideTypeRace, ride.RideTypeTransport}
+
+var bikeTypeLabels = map[ride.BikeType]string{
+ ride.BikeTypeRoad: "Road",
+ ride.BikeTypeMTB: "MTB",
+}
+
+var rideTypeLabels = map[ride.RideType]string{
+ ride.RideTypeTraining: "Training",
+ ride.RideTypeIndoor: "Indoor",
+ ride.RideTypeRace: "Race",
+ ride.RideTypeTransport: "Transport",
+}
+
+func formatBikeType(value ride.BikeType) string {
+ if label, ok := bikeTypeLabels[value]; ok {
+ return label
+ }
+ return string(value)
+}
+
+func formatRideType(value ride.RideType) string {
+ if label, ok := rideTypeLabels[value]; ok {
+ return label
+ }
+ return string(value)
+}
+
+func formatCategoryOrEmpty(value string) string {
+ if value == "" {
+ return "—"
+ }
+ return value
+}