aboutsummaryrefslogtreecommitdiff
path: root/web/ride-filters.go
diff options
context:
space:
mode:
authorMartin Kagamino Lehoux <martin@lehoux.net>2026-08-24 13:36:41 +0200
committerMartin Kagamino Lehoux <martin@lehoux.net>2026-08-24 13:36:41 +0200
commit4dcc0a4e655b0178a7733954bf75df3cff9ef07a (patch)
tree6acca3eba2b94dbed9d759295149cd51551ed9cf /web/ride-filters.go
parente85150b7e1f2191e9ef39c969f041d27aec33643 (diff)
Ride type multiselect filter with a checkbox group across library and analytics
Diffstat (limited to 'web/ride-filters.go')
-rw-r--r--web/ride-filters.go83
1 files changed, 63 insertions, 20 deletions
diff --git a/web/ride-filters.go b/web/ride-filters.go
index de8e242..2282b8d 100644
--- a/web/ride-filters.go
+++ b/web/ride-filters.go
@@ -43,7 +43,7 @@ type RideFilters struct {
CotacolMinPoints *int
CotacolMaxPoints *int
BikeType *ride.BikeType
- RideType *ride.RideType
+ RideTypes []ride.RideType
}
type RideFilterView struct {
@@ -57,7 +57,7 @@ type RideFilterView struct {
CotacolMinPoints int
CotacolMaxPoints int
BikeType string
- RideType string
+ RideTypes []ride.RideType
ResetURL string
}
@@ -140,7 +140,7 @@ func parseRideFilters(query url.Values) RideFilters {
CotacolMinPoints: parseRideFilterInt(query.Get(rideFilterCotacolMinParam), rideFilterCotacolMinPoints, rideFilterCotacolMaxPoints, rideFilterCotacolStepPoints),
CotacolMaxPoints: parseRideFilterInt(query.Get(rideFilterCotacolMaxParam), rideFilterCotacolMinPoints, rideFilterCotacolMaxPoints, rideFilterCotacolStepPoints),
BikeType: parseRideBikeTypeFilter(query.Get(rideFilterBikeTypeParam)),
- RideType: parseRideRideTypeFilter(query.Get(rideFilterRideTypeParam)),
+ RideTypes: parseRideRideTypesFilter(query[rideFilterRideTypeParam]),
DateFrom: parseRideFilterDate(query.Get(rideFilterDateFromParam), false),
DateToExclusive: parseRideFilterDate(query.Get(rideFilterDateToParam), true)}
@@ -212,15 +212,60 @@ func parseRideBikeTypeFilter(value string) *ride.BikeType {
return &bikeType
}
-func parseRideRideTypeFilter(value string) *ride.RideType {
- if value == "" {
- return nil
+func parseRideRideTypesFilter(values []string) []ride.RideType {
+ set := make([]ride.RideType, 0, len(values))
+ for _, value := range values {
+ if !ride.IsValidRideType(value) {
+ continue
+ }
+ rideType := ride.RideType(value)
+ if containsRideType(set, rideType) {
+ continue
+ }
+ set = append(set, rideType)
}
- if !ride.IsValidRideType(value) {
- return nil
+ if len(set) == 0 {
+ return defaultRideTypes()
+ }
+ return set
+}
+
+// defaultRideTypes is the ride type set represented by an absent ride_type
+// filter: every ride type except transport.
+func defaultRideTypes() []ride.RideType {
+ return []ride.RideType{
+ ride.RideTypeTraining,
+ ride.RideTypeIndoor,
+ ride.RideTypeRace,
+ }
+}
+
+func containsRideType(types []ride.RideType, target ride.RideType) bool {
+ for _, candidate := range types {
+ if candidate == target {
+ return true
+ }
}
- rideType := ride.RideType(value)
- return &rideType
+ return false
+}
+
+func rideTypesEqual(a, b []ride.RideType) bool {
+ if len(a) != len(b) {
+ return false
+ }
+ for _, candidate := range a {
+ if !containsRideType(b, candidate) {
+ return false
+ }
+ }
+ return true
+}
+
+func rideTypeFilterActive(types []ride.RideType) bool {
+ if len(types) == 0 {
+ return false
+ }
+ return !rideTypesEqual(types, defaultRideTypes())
}
func buildRideFilterView(filters RideFilters, sort RideSort) RideFilterView {
@@ -236,10 +281,6 @@ func buildRideFilterView(filters RideFilters, sort RideSort) RideFilterView {
if filters.BikeType != nil {
bikeType = string(*filters.BikeType)
}
- rideType := ""
- if filters.RideType != nil {
- rideType = string(*filters.RideType)
- }
return RideFilterView{
Active: filters.Active(),
DistanceMinKm: rideFilterValue(filters.DistanceMinKm, rideFilterDistanceMinKm),
@@ -251,7 +292,7 @@ func buildRideFilterView(filters RideFilters, sort RideSort) RideFilterView {
CotacolMinPoints: rideFilterValue(filters.CotacolMinPoints, rideFilterCotacolMinPoints),
CotacolMaxPoints: rideFilterValue(filters.CotacolMaxPoints, rideFilterCotacolMaxPoints),
BikeType: bikeType,
- RideType: rideType,
+ RideTypes: filters.RideTypes,
ResetURL: rideFiltersResetURL(sort),
}
}
@@ -317,8 +358,10 @@ func rideFilterQuery(filters RideFilters) url.Values {
if filters.BikeType != nil {
query.Set(rideFilterBikeTypeParam, string(*filters.BikeType))
}
- if filters.RideType != nil {
- query.Set(rideFilterRideTypeParam, string(*filters.RideType))
+ if len(filters.RideTypes) > 0 && !rideTypesEqual(filters.RideTypes, defaultRideTypes()) {
+ for _, rideType := range filters.RideTypes {
+ query.Add(rideFilterRideTypeParam, string(rideType))
+ }
}
return query
}
@@ -343,7 +386,7 @@ func (filters RideFilters) Active() bool {
filters.CotacolMinPoints != nil ||
filters.CotacolMaxPoints != nil ||
filters.BikeType != nil ||
- filters.RideType != nil
+ rideTypeFilterActive(filters.RideTypes)
}
func filterRides(items []ride.StoredRide, filters RideFilters) []ride.StoredRide {
@@ -352,7 +395,7 @@ func filterRides(items []ride.StoredRide, filters RideFilters) []ride.StoredRide
if !item.IsCategorised() {
continue
}
- if filters.Active() && !rideMatchesFilters(item, filters) {
+ if !rideMatchesFilters(item, filters) {
continue
}
filtered = append(filtered, item)
@@ -395,7 +438,7 @@ func rideMatchesFilters(item ride.StoredRide, filters RideFilters) bool {
if filters.BikeType != nil && item.BikeType != *filters.BikeType {
return false
}
- if filters.RideType != nil && item.RideType != *filters.RideType {
+ if len(filters.RideTypes) > 0 && !containsRideType(filters.RideTypes, item.RideType) {
return false
}
return true