summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Kagamino Lehoux <martin@lehoux.net>2026-08-18 21:32:28 +0200
committerMartin Kagamino Lehoux <martin@lehoux.net>2026-08-18 21:32:28 +0200
commit992c36c6c7ce7e1e732866b3cd4fb8269a905875 (patch)
tree15b7ae06b14341929e555ebc126f07d963213104
parent51613971c1e25dedbc808c66b79d15111d250160 (diff)
feat(web): add ride library filtersHEADmain
-rw-r--r--README.md2
-rw-r--r--web/frontend/ride-filters-logic.test.ts14
-rw-r--r--web/frontend/ride-filters-logic.ts15
-rw-r--r--web/frontend/ride-filters.ts49
-rw-r--r--web/ride-filters.go307
-rw-r--r--web/ride-filters_test.go256
-rw-r--r--web/server.go12
-rw-r--r--web/server_test.go3
-rw-r--r--web/static/style.css27
-rw-r--r--web/templates.templ87
-rw-r--r--web/templates_templ.go1010
-rw-r--r--web/views.go26
12 files changed, 1560 insertions, 248 deletions
diff --git a/README.md b/README.md
index 2fe84f0..24932e8 100644
--- a/README.md
+++ b/README.md
@@ -17,6 +17,7 @@ A Go toolkit for analyzing cycling rides from GPX exports: parse rides, detect c
- **Strava stream metrics** — preserves heart rate, cadence, and power in Garmin-compatible GPX files and in-memory ride columns
- **Metadata-only ride imports** — stores Strava activity statistics for rides without GPS tracks, without Cotacol or route data
- **Ride table sorting** — sorts every ride-library column through clickable server-side headers and query parameters
+- **Ride library filters** — filters rides by distance, moving duration, start date, Cotacol score, and route availability through shareable query URLs
- **Ride detail maps** — opens a stored ride with its recorded route on an interactive OpenStreetMap map
- **Strava import progress** — streams live import progress with an aggregate progress bar and prevents overlapping syncs
@@ -144,7 +145,6 @@ TypeScript checks can also be run directly with `npm run check`, covering format
- Destination via Start
- Planning rides
- Mountain pass from OSM
-- Rides filters (duration slider, distance slider)
## Resources
diff --git a/web/frontend/ride-filters-logic.test.ts b/web/frontend/ride-filters-logic.test.ts
new file mode 100644
index 0000000..5225028
--- /dev/null
+++ b/web/frontend/ride-filters-logic.test.ts
@@ -0,0 +1,14 @@
+import assert from "node:assert/strict";
+import test from "node:test";
+import { formatRideFilterValue, normalizeRideFilterRange } from "./ride-filters-logic.ts";
+
+test("formats ride filter values", () => {
+ assert.equal(formatRideFilterValue("distance", 120), "120 km");
+ assert.equal(formatRideFilterValue("duration", 150), "2h 30m");
+ assert.equal(formatRideFilterValue("cotacol", 500), "500 points");
+});
+
+test("keeps range handles ordered when the minimum moves past the maximum", () => {
+ assert.deepEqual(normalizeRideFilterRange(200, 100, "min"), { min: 100, max: 100 });
+ assert.deepEqual(normalizeRideFilterRange(200, 100, "max"), { min: 200, max: 200 });
+});
diff --git a/web/frontend/ride-filters-logic.ts b/web/frontend/ride-filters-logic.ts
new file mode 100644
index 0000000..f3593c1
--- /dev/null
+++ b/web/frontend/ride-filters-logic.ts
@@ -0,0 +1,15 @@
+export type RideFilterRangeFormat = "distance" | "duration" | "cotacol";
+export type RideFilterRangeHandle = "min" | "max";
+
+export const formatRideFilterValue = (format: RideFilterRangeFormat, value: number): string => {
+ if (format === "distance") return `${value} km`;
+ if (format === "cotacol") return `${value} points`;
+ const hours = Math.floor(value / 60);
+ const minutes = value % 60;
+ return `${hours}h ${String(minutes).padStart(2, "0")}m`;
+};
+
+export const normalizeRideFilterRange = (min: number, max: number, changed: RideFilterRangeHandle): { min: number; max: number } => {
+ if (min <= max) return { min, max };
+ return changed === "min" ? { min: max, max } : { min, max: min };
+};
diff --git a/web/frontend/ride-filters.ts b/web/frontend/ride-filters.ts
new file mode 100644
index 0000000..24c23e8
--- /dev/null
+++ b/web/frontend/ride-filters.ts
@@ -0,0 +1,49 @@
+import { requireElement } from "./dom.js";
+import {
+ formatRideFilterValue,
+ normalizeRideFilterRange,
+ type RideFilterRangeFormat,
+ type RideFilterRangeHandle,
+} from "./ride-filters-logic.js";
+
+const mountRangeFilter = (container: HTMLElement): void => {
+ const format = container.dataset.rangeFormat as RideFilterRangeFormat | undefined;
+ if (!format) throw new Error("Missing ride filter range format");
+ const minInput = requireElement(container.querySelector<HTMLInputElement>("[data-range-min]"), "ride filter minimum input");
+ const maxInput = requireElement(container.querySelector<HTMLInputElement>("[data-range-max]"), "ride filter maximum input");
+ const track = requireElement(container.querySelector<HTMLElement>("[data-range-track]"), "ride filter range track");
+ const group = requireElement(container.parentElement, "ride filter group");
+ const minOutput = requireElement(group.querySelector<HTMLOutputElement>('[data-range-value="min"]'), "ride filter minimum output");
+ const maxOutput = requireElement(group.querySelector<HTMLOutputElement>('[data-range-value="max"]'), "ride filter maximum output");
+ const lowerBound = Number(minInput.min);
+ const upperBound = Number(minInput.max);
+
+ const update = (changed?: RideFilterRangeHandle): void => {
+ const range = normalizeRideFilterRange(Number(minInput.value), Number(maxInput.value), changed ?? "min");
+ minInput.value = String(range.min);
+ maxInput.value = String(range.max);
+ const span = Math.max(upperBound - lowerBound, 1);
+ const start = ((range.min - lowerBound) / span) * 100;
+ const end = ((range.max - lowerBound) / span) * 100;
+ track.style.setProperty("--range-start", `${start}%`);
+ track.style.setProperty("--range-end", `${end}%`);
+ const minLabel = formatRideFilterValue(format, range.min);
+ const maxLabel = formatRideFilterValue(format, range.max);
+ minOutput.value = minLabel;
+ maxOutput.value = maxLabel;
+ minOutput.textContent = minLabel;
+ maxOutput.textContent = maxLabel;
+ minInput.setAttribute("aria-valuetext", minLabel);
+ maxInput.setAttribute("aria-valuetext", maxLabel);
+ };
+
+ minInput.addEventListener("input", () => update("min"));
+ maxInput.addEventListener("input", () => update("max"));
+ update();
+};
+
+export const mountRideFilters = (): void => {
+ document.querySelectorAll<HTMLElement>("[data-range-filter]").forEach(mountRangeFilter);
+};
+
+mountRideFilters();
diff --git a/web/ride-filters.go b/web/ride-filters.go
new file mode 100644
index 0000000..189a72e
--- /dev/null
+++ b/web/ride-filters.go
@@ -0,0 +1,307 @@
+package web
+
+import (
+ "net/url"
+ "strconv"
+ "time"
+
+ "github.com/martinlehoux/biking_home/ride"
+ "github.com/martinlehoux/biking_home/rides"
+)
+
+const (
+ rideFilterDistanceMinKm = 10
+ rideFilterDistanceMaxKm = 300
+ rideFilterDistanceStepKm = 10
+ rideFilterDurationMinMinutes = 0
+ rideFilterDurationMaxMinutes = 24 * 60
+ rideFilterDurationStepMinutes = 30
+ rideFilterCotacolMinPoints = 0
+ rideFilterCotacolMaxPoints = 5000
+ rideFilterCotacolStepPoints = 50
+)
+
+const (
+ rideFilterDistanceMinParam = "distance_min"
+ rideFilterDistanceMaxParam = "distance_max"
+ rideFilterDurationMinParam = "duration_min"
+ rideFilterDurationMaxParam = "duration_max"
+ rideFilterDateFromParam = "date_from"
+ rideFilterDateToParam = "date_to"
+ rideFilterCotacolMinParam = "cotacol_min"
+ rideFilterCotacolMaxParam = "cotacol_max"
+ rideFilterRouteParam = "route"
+)
+
+const (
+ rideRouteFilterAny = ""
+ rideRouteFilterAvailable = "available"
+ rideRouteFilterMissing = "missing"
+)
+
+type RideFilters struct {
+ DistanceMinKm *int
+ DistanceMaxKm *int
+ DurationMinMinutes *int
+ DurationMaxMinutes *int
+ DateFrom *time.Time
+ DateToExclusive *time.Time
+ CotacolMinPoints *int
+ CotacolMaxPoints *int
+ Route string
+}
+
+type RideFilterView struct {
+ Active bool
+ DistanceMinKm int
+ DistanceMaxKm int
+ DurationMinMinutes int
+ DurationMaxMinutes int
+ DateFrom string
+ DateTo string
+ CotacolMinPoints int
+ CotacolMaxPoints int
+ Route string
+ ResetURL string
+}
+
+func parseRideFilters(query url.Values) RideFilters {
+ filters := RideFilters{
+ DistanceMinKm: parseRideFilterInt(query.Get(rideFilterDistanceMinParam), rideFilterDistanceMinKm, rideFilterDistanceMaxKm, rideFilterDistanceStepKm),
+ DistanceMaxKm: parseRideFilterInt(query.Get(rideFilterDistanceMaxParam), rideFilterDistanceMinKm, rideFilterDistanceMaxKm, rideFilterDistanceStepKm),
+ DurationMinMinutes: parseRideFilterInt(query.Get(rideFilterDurationMinParam), rideFilterDurationMinMinutes, rideFilterDurationMaxMinutes, rideFilterDurationStepMinutes),
+ DurationMaxMinutes: parseRideFilterInt(query.Get(rideFilterDurationMaxParam), rideFilterDurationMinMinutes, rideFilterDurationMaxMinutes, rideFilterDurationStepMinutes),
+ CotacolMinPoints: parseRideFilterInt(query.Get(rideFilterCotacolMinParam), rideFilterCotacolMinPoints, rideFilterCotacolMaxPoints, rideFilterCotacolStepPoints),
+ CotacolMaxPoints: parseRideFilterInt(query.Get(rideFilterCotacolMaxParam), rideFilterCotacolMinPoints, rideFilterCotacolMaxPoints, rideFilterCotacolStepPoints),
+ Route: parseRideRouteFilter(query.Get(rideFilterRouteParam)),
+ }
+ filters.DateFrom = parseRideFilterDate(query.Get(rideFilterDateFromParam), false)
+ filters.DateToExclusive = parseRideFilterDate(query.Get(rideFilterDateToParam), true)
+ filters.DistanceMinKm = clearRideFilterBound(filters.DistanceMinKm, rideFilterDistanceMinKm)
+ filters.DistanceMaxKm = clearRideFilterBound(filters.DistanceMaxKm, rideFilterDistanceMaxKm)
+ filters.DurationMinMinutes = clearRideFilterBound(filters.DurationMinMinutes, rideFilterDurationMinMinutes)
+ filters.DurationMaxMinutes = clearRideFilterBound(filters.DurationMaxMinutes, rideFilterDurationMaxMinutes)
+ filters.CotacolMinPoints = clearRideFilterBound(filters.CotacolMinPoints, rideFilterCotacolMinPoints)
+ filters.CotacolMaxPoints = clearRideFilterBound(filters.CotacolMaxPoints, rideFilterCotacolMaxPoints)
+ if filters.DateFrom != nil && filters.DateToExclusive != nil && !filters.DateFrom.Before(*filters.DateToExclusive) {
+ filters.DateFrom = nil
+ filters.DateToExclusive = nil
+ }
+ if filters.DistanceMinKm != nil && filters.DistanceMaxKm != nil && *filters.DistanceMinKm > *filters.DistanceMaxKm {
+ filters.DistanceMinKm = nil
+ filters.DistanceMaxKm = nil
+ }
+ if filters.DurationMinMinutes != nil && filters.DurationMaxMinutes != nil && *filters.DurationMinMinutes > *filters.DurationMaxMinutes {
+ filters.DurationMinMinutes = nil
+ filters.DurationMaxMinutes = nil
+ }
+ if filters.CotacolMinPoints != nil && filters.CotacolMaxPoints != nil && *filters.CotacolMinPoints > *filters.CotacolMaxPoints {
+ filters.CotacolMinPoints = nil
+ filters.CotacolMaxPoints = nil
+ }
+ return filters
+}
+
+func clearRideFilterBound(value *int, bound int) *int {
+ if value != nil && *value == bound {
+ return nil
+ }
+ return value
+}
+
+func parseRideFilterInt(value string, min, max, step int) *int {
+ if value == "" {
+ return nil
+ }
+ parsed, err := strconv.Atoi(value)
+ if err != nil || parsed < min || parsed > max || (parsed-min)%step != 0 {
+ return nil
+ }
+ return &parsed
+}
+
+func parseRideFilterDate(value string, end bool) *time.Time {
+ if value == "" {
+ return nil
+ }
+ parsed, err := time.Parse(dateFormat, value)
+ if err != nil {
+ return nil
+ }
+ if end {
+ parsed = parsed.AddDate(0, 0, 1)
+ }
+ return &parsed
+}
+
+func parseRideRouteFilter(value string) string {
+ if value == rideRouteFilterAvailable || value == rideRouteFilterMissing {
+ return value
+ }
+ return rideRouteFilterAny
+}
+
+func buildRideFilterView(filters RideFilters, sort RideSort) RideFilterView {
+ dateFrom := ""
+ if filters.DateFrom != nil {
+ dateFrom = filters.DateFrom.Format(dateFormat)
+ }
+ dateTo := ""
+ if filters.DateToExclusive != nil {
+ dateTo = filters.DateToExclusive.AddDate(0, 0, -1).Format(dateFormat)
+ }
+ return RideFilterView{
+ Active: filters.Active(),
+ DistanceMinKm: rideFilterValue(filters.DistanceMinKm, rideFilterDistanceMinKm),
+ DistanceMaxKm: rideFilterValue(filters.DistanceMaxKm, rideFilterDistanceMaxKm),
+ DurationMinMinutes: rideFilterValue(filters.DurationMinMinutes, rideFilterDurationMinMinutes),
+ DurationMaxMinutes: rideFilterValue(filters.DurationMaxMinutes, rideFilterDurationMaxMinutes),
+ DateFrom: dateFrom,
+ DateTo: dateTo,
+ CotacolMinPoints: rideFilterValue(filters.CotacolMinPoints, rideFilterCotacolMinPoints),
+ CotacolMaxPoints: rideFilterValue(filters.CotacolMaxPoints, rideFilterCotacolMaxPoints),
+ Route: filters.Route,
+ ResetURL: rideFiltersResetURL(sort),
+ }
+}
+
+func rideFilterValue(value *int, fallback int) int {
+ if value == nil {
+ return fallback
+ }
+ return *value
+}
+
+func formatRideFilterInt(value int) string {
+ return strconv.Itoa(value)
+}
+
+func formatRideFilterDistance(value int) string {
+ return strconv.Itoa(value) + " km"
+}
+
+func formatRideFilterDuration(value int) string {
+ hours := value / 60
+ minutes := value % 60
+ return strconv.Itoa(hours) + "h " + formatTwoDigitNumber(minutes) + "m"
+}
+
+func formatRideFilterCotacol(value int) string {
+ return strconv.Itoa(value) + " points"
+}
+
+func formatTwoDigitNumber(value int) string {
+ if value < 10 {
+ return "0" + strconv.Itoa(value)
+ }
+ return strconv.Itoa(value)
+}
+
+func rideFilterQuery(filters RideFilters) url.Values {
+ query := url.Values{}
+ if filters.DistanceMinKm != nil {
+ query.Set(rideFilterDistanceMinParam, formatRideFilterInt(*filters.DistanceMinKm))
+ }
+ if filters.DistanceMaxKm != nil {
+ query.Set(rideFilterDistanceMaxParam, formatRideFilterInt(*filters.DistanceMaxKm))
+ }
+ if filters.DurationMinMinutes != nil {
+ query.Set(rideFilterDurationMinParam, formatRideFilterInt(*filters.DurationMinMinutes))
+ }
+ if filters.DurationMaxMinutes != nil {
+ query.Set(rideFilterDurationMaxParam, formatRideFilterInt(*filters.DurationMaxMinutes))
+ }
+ if filters.DateFrom != nil {
+ query.Set(rideFilterDateFromParam, filters.DateFrom.Format(dateFormat))
+ }
+ if filters.DateToExclusive != nil {
+ query.Set(rideFilterDateToParam, filters.DateToExclusive.AddDate(0, 0, -1).Format(dateFormat))
+ }
+ if filters.CotacolMinPoints != nil {
+ query.Set(rideFilterCotacolMinParam, formatRideFilterInt(*filters.CotacolMinPoints))
+ }
+ if filters.CotacolMaxPoints != nil {
+ query.Set(rideFilterCotacolMaxParam, formatRideFilterInt(*filters.CotacolMaxPoints))
+ }
+ if filters.Route != rideRouteFilterAny {
+ query.Set(rideFilterRouteParam, filters.Route)
+ }
+ return query
+}
+
+func rideFiltersResetURL(sort RideSort) string {
+ if sort.Column == rideSortStarted && sort.Descending {
+ return "/"
+ }
+ query := url.Values{}
+ query.Set("sort", sort.Column)
+ query.Set("dir", rideSortDirection(sort))
+ return "/?" + query.Encode()
+}
+
+func (filters RideFilters) Active() bool {
+ return filters.DistanceMinKm != nil ||
+ filters.DistanceMaxKm != nil ||
+ filters.DurationMinMinutes != nil ||
+ filters.DurationMaxMinutes != nil ||
+ filters.DateFrom != nil ||
+ filters.DateToExclusive != nil ||
+ filters.CotacolMinPoints != nil ||
+ filters.CotacolMaxPoints != nil ||
+ filters.Route != rideRouteFilterAny
+}
+
+func filterRides(items []rides.Ride, filters RideFilters) []rides.Ride {
+ if !filters.Active() {
+ return items
+ }
+ filtered := make([]rides.Ride, 0, len(items))
+ for _, item := range items {
+ if rideMatchesFilters(item, filters) {
+ filtered = append(filtered, item)
+ }
+ }
+ return filtered
+}
+
+func rideMatchesFilters(item rides.Ride, filters RideFilters) bool {
+ if filters.DistanceMinKm != nil && item.DistanceM < float64(*filters.DistanceMinKm)*1000 {
+ return false
+ }
+ if filters.DistanceMaxKm != nil && item.DistanceM > float64(*filters.DistanceMaxKm)*1000 {
+ return false
+ }
+ if filters.DurationMinMinutes != nil && item.MovingTimeS < int64(*filters.DurationMinMinutes)*60 {
+ return false
+ }
+ if filters.DurationMaxMinutes != nil && item.MovingTimeS > int64(*filters.DurationMaxMinutes)*60 {
+ return false
+ }
+ startDate := item.StartDate.UTC()
+ if filters.DateFrom != nil && startDate.Before(*filters.DateFrom) {
+ return false
+ }
+ if filters.DateToExclusive != nil && !startDate.Before(*filters.DateToExclusive) {
+ return false
+ }
+ if filters.CotacolMinPoints != nil || filters.CotacolMaxPoints != nil {
+ cotacol, ready := item.CotacolScore()
+ if !ready || item.CotacolAlgorithmVersion() != ride.CotacolAlgorithmVersion || item.DistanceM <= 0 {
+ return false
+ }
+ if filters.CotacolMinPoints != nil && cotacol < float64(*filters.CotacolMinPoints) {
+ return false
+ }
+ if filters.CotacolMaxPoints != nil && cotacol > float64(*filters.CotacolMaxPoints) {
+ return false
+ }
+ }
+ switch filters.Route {
+ case rideRouteFilterAvailable:
+ return item.GPXPath != ""
+ case rideRouteFilterMissing:
+ return item.GPXPath == ""
+ default:
+ return true
+ }
+}
diff --git a/web/ride-filters_test.go b/web/ride-filters_test.go
new file mode 100644
index 0000000..c7b9133
--- /dev/null
+++ b/web/ride-filters_test.go
@@ -0,0 +1,256 @@
+package web
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "net/url"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/martinlehoux/biking_home/ride"
+ "github.com/martinlehoux/biking_home/rides"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestParseRideFilters(t *testing.T) {
+ query := url.Values{
+ rideFilterDistanceMinParam: {"20"},
+ rideFilterDistanceMaxParam: {"100"},
+ rideFilterDurationMinParam: {"30"},
+ rideFilterDurationMaxParam: {"180"},
+ rideFilterDateFromParam: {"2026-08-01"},
+ rideFilterDateToParam: {"2026-08-31"},
+ rideFilterCotacolMinParam: {"50"},
+ rideFilterCotacolMaxParam: {"500"},
+ rideFilterRouteParam: {rideRouteFilterAvailable},
+ }
+
+ filters := parseRideFilters(query)
+
+ assert.Equal(t, 20, *filters.DistanceMinKm)
+ assert.Equal(t, 100, *filters.DistanceMaxKm)
+ assert.Equal(t, 30, *filters.DurationMinMinutes)
+ assert.Equal(t, 180, *filters.DurationMaxMinutes)
+ assert.Equal(t, "2026-08-01T00:00:00Z", filters.DateFrom.Format(time.RFC3339))
+ assert.Equal(t, "2026-09-01T00:00:00Z", filters.DateToExclusive.Format(time.RFC3339))
+ assert.Equal(t, 50, *filters.CotacolMinPoints)
+ assert.Equal(t, 500, *filters.CotacolMaxPoints)
+ assert.Equal(t, rideRouteFilterAvailable, filters.Route)
+ assert.True(t, filters.Active())
+}
+
+func TestParseRideFiltersIgnoresInvalidValues(t *testing.T) {
+ query := url.Values{
+ rideFilterDistanceMinParam: {"15"},
+ rideFilterDistanceMaxParam: {"305"},
+ rideFilterDurationMinParam: {"31"},
+ rideFilterDurationMaxParam: {"31"},
+ rideFilterDateFromParam: {"2026-09-01"},
+ rideFilterDateToParam: {"2026-08-01"},
+ rideFilterCotacolMinParam: {"25"},
+ rideFilterCotacolMaxParam: {"25"},
+ rideFilterRouteParam: {"unknown"},
+ }
+
+ filters := parseRideFilters(query)
+
+ assert.False(t, filters.Active())
+ assert.Nil(t, filters.DistanceMinKm)
+ assert.Nil(t, filters.DistanceMaxKm)
+ assert.Nil(t, filters.DurationMinMinutes)
+ assert.Nil(t, filters.DurationMaxMinutes)
+ assert.Nil(t, filters.DateFrom)
+ assert.Nil(t, filters.DateToExclusive)
+ assert.Nil(t, filters.CotacolMinPoints)
+ assert.Nil(t, filters.CotacolMaxPoints)
+ assert.Empty(t, filters.Route)
+}
+
+func TestParseRideFiltersTreatsFullRangesAsInactive(t *testing.T) {
+ filters := parseRideFilters(url.Values{
+ rideFilterDistanceMinParam: {"10"},
+ rideFilterDistanceMaxParam: {"300"},
+ rideFilterDurationMinParam: {"0"},
+ rideFilterDurationMaxParam: {"1440"},
+ rideFilterCotacolMinParam: {"0"},
+ rideFilterCotacolMaxParam: {"5000"},
+ })
+
+ assert.False(t, filters.Active())
+ assert.Nil(t, filters.DistanceMinKm)
+ assert.Nil(t, filters.DistanceMaxKm)
+ assert.Nil(t, filters.DurationMinMinutes)
+ assert.Nil(t, filters.DurationMaxMinutes)
+ assert.Nil(t, filters.CotacolMinPoints)
+ assert.Nil(t, filters.CotacolMaxPoints)
+}
+
+func TestFilterRidesUsesInclusiveBoundsAndMovingTime(t *testing.T) {
+ items := []rides.Ride{
+ {Name: "lower boundary", DistanceM: 20_000, MovingTimeS: 1_800, StartDate: time.Date(2026, 8, 1, 12, 0, 0, 0, time.UTC), GPXPath: "lower.gpx"},
+ {Name: "upper boundary", DistanceM: 100_000, MovingTimeS: 10_800, StartDate: time.Date(2026, 8, 31, 12, 0, 0, 0, time.UTC), GPXPath: "upper.gpx"},
+ {Name: "outside distance", DistanceM: 110_000, MovingTimeS: 10_800, StartDate: time.Date(2026, 8, 31, 12, 0, 0, 0, time.UTC), GPXPath: "outside-distance.gpx"},
+ {Name: "outside moving time", DistanceM: 100_000, MovingTimeS: 10_801, StartDate: time.Date(2026, 8, 31, 12, 0, 0, 0, time.UTC), GPXPath: "outside-duration.gpx"},
+ {Name: "outside date", DistanceM: 100_000, MovingTimeS: 10_800, StartDate: time.Date(2026, 9, 1, 12, 0, 0, 0, time.UTC), GPXPath: "outside-date.gpx"},
+ }
+ filters := parseRideFilters(url.Values{
+ rideFilterDistanceMinParam: {"20"},
+ rideFilterDistanceMaxParam: {"100"},
+ rideFilterDurationMinParam: {"30"},
+ rideFilterDurationMaxParam: {"180"},
+ rideFilterDateFromParam: {"2026-08-01"},
+ rideFilterDateToParam: {"2026-08-31"},
+ })
+
+ filtered := filterRides(items, filters)
+
+ require.Len(t, filtered, 2)
+ assert.Equal(t, "lower boundary", filtered[0].Name)
+ assert.Equal(t, "upper boundary", filtered[1].Name)
+}
+
+func TestFilterRidesByRouteAvailability(t *testing.T) {
+ items := []rides.Ride{
+ {Name: "recorded", GPXPath: "recorded.gpx"},
+ {Name: "metadata only"},
+ }
+
+ available := filterRides(items, parseRideFilters(url.Values{rideFilterRouteParam: {rideRouteFilterAvailable}}))
+ missing := filterRides(items, parseRideFilters(url.Values{rideFilterRouteParam: {rideRouteFilterMissing}}))
+
+ require.Len(t, available, 1)
+ assert.Equal(t, "recorded", available[0].Name)
+ require.Len(t, missing, 1)
+ assert.Equal(t, "metadata only", missing[0].Name)
+}
+
+func TestFilterRidesByCotacolExcludesUnavailableScores(t *testing.T) {
+ _, db := newWebTestServer(t)
+ current := rides.Ride{
+ ExternalID: "strava:550e8400-e29b-41d4-a716-446655440000",
+ GPXPath: testGPXPath(t, "current-cotacol.gpx"),
+ Name: "Current score",
+ Type: "Ride",
+ StartDate: time.Date(2026, 8, 1, 7, 0, 0, 0, time.UTC),
+ DistanceM: 20_000,
+ }
+ stale := rides.Ride{
+ ExternalID: "strava:6ba7b810-9dad-41d1-80b4-00c04fd430c8",
+ GPXPath: testGPXPath(t, "stale-cotacol.gpx"),
+ Name: "Stale score",
+ Type: "Ride",
+ StartDate: time.Date(2026, 8, 2, 7, 0, 0, 0, time.UTC),
+ DistanceM: 20_000,
+ }
+ unavailable := rides.Ride{
+ ExternalID: "strava:7ba7b810-9dad-41d1-80b4-00c04fd430c8",
+ Name: "No score",
+ Type: "VirtualRide",
+ StartDate: time.Date(2026, 8, 3, 7, 0, 0, 0, time.UTC),
+ DistanceM: 20_000,
+ }
+ require.NoError(t, rides.Save(db, current))
+ require.NoError(t, rides.Save(db, stale))
+ require.NoError(t, rides.Save(db, unavailable))
+ _, err := db.Exec(`
+ UPDATE rides
+ SET cotacol_score = CASE external_id
+ WHEN ? THEN 250
+ WHEN ? THEN 250
+ END,
+ cotacol_algo_version = CASE external_id
+ WHEN ? THEN ?
+ WHEN ? THEN 'old'
+ END
+ WHERE external_id IN (?, ?)
+ `, current.ExternalID, stale.ExternalID, current.ExternalID, ride.CotacolAlgorithmVersion, stale.ExternalID, current.ExternalID, stale.ExternalID)
+ require.NoError(t, err)
+ items, err := rides.List(db)
+ require.NoError(t, err)
+ filters := parseRideFilters(url.Values{rideFilterCotacolMinParam: {"200"}, rideFilterCotacolMaxParam: {"300"}})
+
+ filtered := filterRides(items, filters)
+
+ require.Len(t, filtered, 1)
+ assert.Equal(t, current.ExternalID, filtered[0].ExternalID)
+}
+
+func TestHandlerRendersRideFilterControls(t *testing.T) {
+ server, _ := newWebTestServer(t)
+ request := httptest.NewRequest(http.MethodGet, "/", nil)
+ response := httptest.NewRecorder()
+
+ server.Handler().ServeHTTP(response, request)
+
+ body := response.Body.String()
+ assert.Equal(t, http.StatusOK, response.Code)
+ assert.Contains(t, body, `id="ride-filters"`)
+ assert.Contains(t, body, `name="distance_min"`)
+ assert.Contains(t, body, `name="duration_min"`)
+ assert.Contains(t, body, `name="date_from"`)
+ assert.Contains(t, body, `name="cotacol_min"`)
+ assert.Contains(t, body, `name="route"`)
+ assert.Contains(t, body, `<script type="module" src="/static/ride-filters.js"></script>`)
+}
+
+func TestRideSortHeadersPreserveFilters(t *testing.T) {
+ filters := parseRideFilters(url.Values{
+ rideFilterDistanceMinParam: {"50"},
+ rideFilterDistanceMaxParam: {"150"},
+ rideFilterRouteParam: {rideRouteFilterAvailable},
+ })
+
+ headers := rideSortHeadersWithFilters(RideSort{Column: rideSortDistance, Descending: true}, filters)
+
+ assert.Equal(t, "/?dir=asc&distance_max=150&distance_min=50&route=available&sort=name", headers[0].URL)
+}
+
+func TestHandlerFiltersRidesBeforeRenderingSortedResults(t *testing.T) {
+ server, db := newWebTestServer(t)
+ save := func(externalID, name, path string, distance float64) {
+ require.NoError(t, rides.Save(db, rides.Ride{
+ ExternalID: externalID,
+ GPXPath: testGPXPath(t, path),
+ Name: name,
+ Type: "Ride",
+ StartDate: time.Date(2026, 8, 1, 7, 0, 0, 0, time.UTC),
+ DistanceM: distance,
+ }))
+ }
+ save("strava:550e8400-e29b-41d4-a716-446655440000", "Long Ride", "long-filtered.gpx", 40_000)
+ save("strava:6ba7b810-9dad-41d1-80b4-00c04fd430c8", "Short Ride", "short-filtered.gpx", 20_000)
+ save("strava:7ba7b810-9dad-41d1-80b4-00c04fd430c8", "Too Far Ride", "far-filtered.gpx", 320_000)
+
+ request := httptest.NewRequest(http.MethodGet, "/?distance_min=10&distance_max=100&sort=distance&dir=asc", nil)
+ response := httptest.NewRecorder()
+ server.Handler().ServeHTTP(response, request)
+
+ body := response.Body.String()
+ assert.Equal(t, http.StatusOK, response.Code)
+ assert.Less(t, strings.Index(body, "Short Ride"), strings.Index(body, "Long Ride"))
+ assert.NotContains(t, body, "Too Far Ride")
+}
+
+func TestHandlerRendersFilteredEmptyState(t *testing.T) {
+ server, db := newWebTestServer(t)
+ require.NoError(t, rides.Save(db, rides.Ride{
+ ExternalID: "strava:8ba7b810-9dad-41d1-80b4-00c04fd430c8",
+ GPXPath: testGPXPath(t, "empty-filter.gpx"),
+ Name: "Short Ride",
+ Type: "Ride",
+ StartDate: time.Date(2026, 8, 1, 7, 0, 0, 0, time.UTC),
+ DistanceM: 20_000,
+ }))
+
+ request := httptest.NewRequest(http.MethodGet, "/?distance_min=300&distance_max=300", nil)
+ response := httptest.NewRecorder()
+ server.Handler().ServeHTTP(response, request)
+
+ body := response.Body.String()
+ assert.Equal(t, http.StatusOK, response.Code)
+ assert.Contains(t, body, "No rides match the selected filters.")
+ assert.Contains(t, body, `href="/">Clear filters.</a>`)
+ assert.NotContains(t, body, "No rides stored yet.")
+}
diff --git a/web/server.go b/web/server.go
index 496e44d..4bc74e3 100644
--- a/web/server.go
+++ b/web/server.go
@@ -67,14 +67,24 @@ func (s *Server) ListenAndServe(addr string) error {
func (s *Server) handleRides(w http.ResponseWriter, r *http.Request) {
rideSort := parseRideSort(r.URL.Query())
+ rideFilters := parseRideFilters(r.URL.Query())
items, err := s.listRides(rideSort)
if err != nil {
slog.Error("Failed to load rides", "sort", rideSort.Column, "descending", rideSort.Descending, "error", err)
http.Error(w, "failed to load rides", http.StatusInternalServerError)
return
}
+ items = filterRides(items, rideFilters)
+ if rideFilters.Active() {
+ slog.Info("Applied ride library filters", "matches", len(items))
+ }
views := buildRideViews(items)
- kcore.RenderPage(r.Context(), RidesPage(views, rideSortHeaders(rideSort)), w)
+ kcore.RenderPage(r.Context(), RidesPage(RidesPageData{
+ Items: views,
+ Headers: rideSortHeadersWithFilters(rideSort, rideFilters),
+ Filters: buildRideFilterView(rideFilters, rideSort),
+ Sort: rideSort,
+ }), w)
}
func (s *Server) handleRide(w http.ResponseWriter, r *http.Request) {
diff --git a/web/server_test.go b/web/server_test.go
index 57f0d4a..823e4a8 100644
--- a/web/server_test.go
+++ b/web/server_test.go
@@ -95,7 +95,8 @@ func TestHandlerRendersRidesPage(t *testing.T) {
server.Handler().ServeHTTP(response, req)
assert.Equal(t, http.StatusOK, response.Code)
- assert.Contains(t, response.Body.String(), "All rides")
+ assert.NotContains(t, response.Body.String(), "All rides")
+ assert.NotContains(t, response.Body.String(), "Your imported rides, ready for climb analysis.")
assert.Contains(t, response.Body.String(), "Long Ride")
assert.NotContains(t, response.Body.String(), "Short Ride")
assert.Contains(t, response.Body.String(), "Cotacol")
diff --git a/web/static/style.css b/web/static/style.css
index b984da8..ef6348b 100644
--- a/web/static/style.css
+++ b/web/static/style.css
@@ -86,6 +86,30 @@ th { color: var(--color-subtle); font-size: .78rem; letter-spacing: .08em; text-
.sort-indicator { display: inline-block; margin-left: .2rem; }
.numeric { text-align: right; white-space: nowrap; }
.empty { padding: 2.5rem 1rem; text-align: center; color: var(--color-subtle); }
+.ride-filters-panel { margin-top: 1.25rem; }
+.ride-filters { display: grid; gap: 1.25rem; margin-top: 1.25rem; }
+.ride-filters > .section-heading { flex-wrap: wrap; }
+.ride-filter-ranges { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1rem 1.5rem; }
+.ride-filter-group { min-width: 0; margin: 0; padding: 0; border: 0; }
+.ride-filter-group legend { padding: 0; color: var(--color-forest); font-size: .9rem; font-weight: 750; }
+.range-values { display: flex; align-items: baseline; justify-content: space-between; gap: .5rem; margin-top: .45rem; color: var(--color-muted); font-size: .85rem; }
+.range-values output { color: var(--color-foreground); font-weight: 700; }
+.range-control { position: relative; height: 2rem; margin: .35rem .55rem 0; }
+.range-track { position: absolute; top: 50%; right: 0; left: 0; height: .35rem; transform: translateY(-50%); border-radius: 999px; background: linear-gradient(to right, var(--color-border-subtle) 0%, var(--color-border-subtle) var(--range-start, 0%), var(--color-accent) var(--range-start, 0%), var(--color-accent) var(--range-end, 100%), var(--color-border-subtle) var(--range-end, 100%), var(--color-border-subtle) 100%); pointer-events: none; }
+.range-input { position: absolute; inset: 0; width: 100%; height: 2rem; margin: 0; padding: 0; border: 0; background: transparent; outline: none; pointer-events: none; touch-action: none; }
+.range-input-min { z-index: 2; }
+.range-input-max { z-index: 1; }
+.range-input::-webkit-slider-runnable-track { height: .35rem; background: transparent; }
+.range-input::-moz-range-track { height: .35rem; background: transparent; }
+.range-input::-webkit-slider-thumb { width: 1rem; height: 1rem; margin-top: -.325rem; border: 2px solid var(--color-surface); border-radius: 50%; background: var(--color-accent); box-shadow: 0 .15rem .4rem var(--color-panel-shadow); appearance: none; pointer-events: auto; cursor: pointer; }
+.range-input::-moz-range-thumb { width: 1rem; height: 1rem; border: 2px solid var(--color-surface); border-radius: 50%; background: var(--color-accent); box-shadow: 0 .15rem .4rem var(--color-panel-shadow); appearance: none; pointer-events: auto; cursor: pointer; }
+.range-input:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 .2rem var(--color-focus); }
+.range-input:focus-visible::-moz-range-thumb { box-shadow: 0 0 0 .2rem var(--color-focus); }
+.ride-filter-meta { display: grid; grid-template-columns: minmax(0, 2fr) minmax(12rem, 1fr); align-items: end; gap: 1rem; }
+.date-filter-inputs { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .75rem; margin-top: .5rem; }
+.route-filter { align-self: end; }
+select { border: 1px solid var(--color-input-border); border-radius: .55rem; padding: .65rem .7rem; font: inherit; color: inherit; background: var(--color-surface); }
+.ride-filter-actions { display: flex; justify-content: flex-end; gap: .75rem; flex-wrap: wrap; }
.map-panel { padding: 0; overflow: hidden; }
.ride-map { min-height: 24rem; height: min(65vh, 40rem); }
.ride-map-pass-icon { display: grid; place-items: center; width: 1.75rem !important; height: 1.75rem !important; border: 2px solid var(--color-surface); border-radius: 50%; background: var(--color-leaf); color: var(--color-on-dark); box-shadow: 0 .15rem .4rem var(--color-panel-shadow); }
@@ -159,4 +183,5 @@ dt { color: var(--color-subtle); font-size: .78rem; font-weight: 700; letter-spa
dd { margin: .25rem 0 0; font-size: 1.2rem; font-weight: 700; }
@media (width <= 900px) { .ride-detail-columns { grid-template-columns: 1fr; } }
@media (width <= 700px) { .container { width: min(100% - 1rem, 70rem); padding-top: 1.5rem; } .site-header { padding-inline: 1rem; } th:nth-child(n+4), td:nth-child(n+4) { display: none; } .panel { padding: .8rem; } }
-@media (width <= 500px) { dl { grid-template-columns: repeat(2, 1fr); } .official-climb-summary-stats { gap: .55rem; } .official-climb-card-title { display: block; } }
+@media (width <= 700px) { .ride-filter-ranges, .ride-filter-meta { grid-template-columns: 1fr; } }
+@media (width <= 500px) { dl { grid-template-columns: repeat(2, 1fr); } .official-climb-summary-stats { gap: .55rem; } .official-climb-card-title { display: block; } .date-filter-inputs { grid-template-columns: 1fr; } }
diff --git a/web/templates.templ b/web/templates.templ
index f3f07e7..8a987fc 100644
--- a/web/templates.templ
+++ b/web/templates.templ
@@ -22,34 +22,99 @@ templ Layout(title string, content templ.Component) {
</html>
}
-templ RidesPage(items []RideView, headers []RideSortHeader) {
- @Layout("Rides", RidesContent(items, headers))
+templ RidesPage(data RidesPageData) {
+ @Layout("Rides", RidesContent(data))
}
-templ RidesContent(items []RideView, headers []RideSortHeader) {
+templ RidesContent(data RidesPageData) {
<p class="eyebrow">Ride library</p>
- <div class="toolbar">
- <div><h1>All rides</h1><p class="lead">Your imported rides, ready for climb analysis.</p></div>
- <a class="button" href="/sync">Sync Strava</a>
- </div>
+ <section class="panel ride-filters-panel">
+ <form id="ride-filters" class="ride-filters" method="get" action="/" aria-label="Ride filters">
+ <div class="section-heading">
+ <div><p class="eyebrow">Refine</p><h2>Filter rides</h2></div>
+ <div class="ride-filter-actions"><button class="button" type="submit">Apply filters</button><a class="button secondary" href={ templ.URL(data.Filters.ResetURL) }>Reset</a></div>
+ </div>
+ <input type="hidden" name="sort" value={ data.Sort.Column }/>
+ <input type="hidden" name="dir" value={ rideSortDirection(data.Sort) }/>
+ <div class="ride-filter-ranges">
+ <fieldset class="ride-filter-group">
+ <legend>Distance</legend>
+ <div class="range-values"><output data-range-value="min" for="distance-min">{ formatRideFilterDistance(data.Filters.DistanceMinKm) }</output><span aria-hidden="true">to</span><output data-range-value="max" for="distance-max">{ formatRideFilterDistance(data.Filters.DistanceMaxKm) }</output></div>
+ <div class="range-control" data-range-filter="distance" data-range-format="distance">
+ <div class="range-track" data-range-track></div>
+ <input id="distance-min" class="range-input range-input-min" type="range" name="distance_min" min={ formatRideFilterInt(rideFilterDistanceMinKm) } max={ formatRideFilterInt(rideFilterDistanceMaxKm) } step={ formatRideFilterInt(rideFilterDistanceStepKm) } value={ formatRideFilterInt(data.Filters.DistanceMinKm) } data-range-min aria-label="Minimum distance"/>
+ <input id="distance-max" class="range-input range-input-max" type="range" name="distance_max" min={ formatRideFilterInt(rideFilterDistanceMinKm) } max={ formatRideFilterInt(rideFilterDistanceMaxKm) } step={ formatRideFilterInt(rideFilterDistanceStepKm) } value={ formatRideFilterInt(data.Filters.DistanceMaxKm) } data-range-max aria-label="Maximum distance"/>
+ </div>
+ </fieldset>
+ <fieldset class="ride-filter-group">
+ <legend>Moving duration</legend>
+ <div class="range-values"><output data-range-value="min" for="duration-min">{ formatRideFilterDuration(data.Filters.DurationMinMinutes) }</output><span aria-hidden="true">to</span><output data-range-value="max" for="duration-max">{ formatRideFilterDuration(data.Filters.DurationMaxMinutes) }</output></div>
+ <div class="range-control" data-range-filter="duration" data-range-format="duration">
+ <div class="range-track" data-range-track></div>
+ <input id="duration-min" class="range-input range-input-min" type="range" name="duration_min" min={ formatRideFilterInt(rideFilterDurationMinMinutes) } max={ formatRideFilterInt(rideFilterDurationMaxMinutes) } step={ formatRideFilterInt(rideFilterDurationStepMinutes) } value={ formatRideFilterInt(data.Filters.DurationMinMinutes) } data-range-min aria-label="Minimum moving duration"/>
+ <input id="duration-max" class="range-input range-input-max" type="range" name="duration_max" min={ formatRideFilterInt(rideFilterDurationMinMinutes) } max={ formatRideFilterInt(rideFilterDurationMaxMinutes) } step={ formatRideFilterInt(rideFilterDurationStepMinutes) } value={ formatRideFilterInt(data.Filters.DurationMaxMinutes) } data-range-max aria-label="Maximum moving duration"/>
+ </div>
+ </fieldset>
+ <fieldset class="ride-filter-group">
+ <legend>Cotacol</legend>
+ <div class="range-values"><output data-range-value="min" for="cotacol-min">{ formatRideFilterCotacol(data.Filters.CotacolMinPoints) }</output><span aria-hidden="true">to</span><output data-range-value="max" for="cotacol-max">{ formatRideFilterCotacol(data.Filters.CotacolMaxPoints) }</output></div>
+ <div class="range-control" data-range-filter="cotacol" data-range-format="cotacol">
+ <div class="range-track" data-range-track></div>
+ <input id="cotacol-min" class="range-input range-input-min" type="range" name="cotacol_min" min={ formatRideFilterInt(rideFilterCotacolMinPoints) } max={ formatRideFilterInt(rideFilterCotacolMaxPoints) } step={ formatRideFilterInt(rideFilterCotacolStepPoints) } value={ formatRideFilterInt(data.Filters.CotacolMinPoints) } data-range-min aria-label="Minimum Cotacol"/>
+ <input id="cotacol-max" class="range-input range-input-max" type="range" name="cotacol_max" min={ formatRideFilterInt(rideFilterCotacolMinPoints) } max={ formatRideFilterInt(rideFilterCotacolMaxPoints) } step={ formatRideFilterInt(rideFilterCotacolStepPoints) } value={ formatRideFilterInt(data.Filters.CotacolMaxPoints) } data-range-max aria-label="Maximum Cotacol"/>
+ </div>
+ </fieldset>
+ </div>
+ <div class="ride-filter-meta">
+ <fieldset class="ride-filter-group date-filter-group">
+ <legend>Started</legend>
+ <div class="date-filter-inputs"><label for="ride-date-from">From<input id="ride-date-from" type="date" name="date_from" value={ data.Filters.DateFrom }/></label><label for="ride-date-to">To<input id="ride-date-to" type="date" name="date_to" value={ data.Filters.DateTo }/></label></div>
+ </fieldset>
+ <label class="route-filter" for="ride-route">Route
+ <select id="ride-route" name="route">
+ if data.Filters.Route == rideRouteFilterAny {
+ <option value="" selected>Any route</option>
+ } else {
+ <option value="">Any route</option>
+ }
+ if data.Filters.Route == rideRouteFilterAvailable {
+ <option value={ rideRouteFilterAvailable } selected>Recorded route</option>
+ } else {
+ <option value={ rideRouteFilterAvailable }>Recorded route</option>
+ }
+ if data.Filters.Route == rideRouteFilterMissing {
+ <option value={ rideRouteFilterMissing } selected>Metadata only</option>
+ } else {
+ <option value={ rideRouteFilterMissing }>Metadata only</option>
+ }
+ </select>
+ </label>
+ </div>
+ </form>
+ </section>
<section class="panel">
- if len(items) == 0 {
- <div class="empty">No rides stored yet. <a href="/sync">Import your first Strava rides.</a></div>
+ if len(data.Items) == 0 {
+ if data.Filters.Active {
+ <div class="empty">No rides match the selected filters. <a href={ templ.URL(data.Filters.ResetURL) }>Clear filters.</a></div>
+ } else {
+ <div class="empty">No rides stored yet. <a href="/sync">Import your first Strava rides.</a></div>
+ }
} else {
<table>
<thead><tr>
- for _, header := range headers {
+ for _, header := range data.Headers {
<th class={ header.Class } scope="col" aria-sort={ header.AriaSort }><a class="sort-link" href={ templ.URL(header.URL) }>{ header.Label }<span class="sort-indicator" aria-hidden="true">{ header.Indicator }</span></a></th>
}
</tr></thead>
<tbody>
- for _, item := range items {
+ for _, item := range data.Items {
<tr><td><strong><a href={ templ.URL(rideDetailURL(item.ID)) }>{ item.Name }</a></strong><br/><small>{ item.Type }</small></td><td>{ formatRideDate(item.StartDate) }</td><td class="numeric">{ formatDistance(item.DistanceM) }</td><td class="numeric">{ formatDuration(item.MovingTimeS) }</td><td class="numeric">{ formatElevation(item.TotalElevationGainM) }</td><td class="numeric">{ item.Cotacol }</td><td class="numeric">{ item.CotacolPer100Km }</td></tr>
}
</tbody>
</table>
}
</section>
+ <script type="module" src="/static/ride-filters.js"></script>
}
templ RideDetailPage(data RideDetailView) {
diff --git a/web/templates_templ.go b/web/templates_templ.go
index b16554d..05ae20a 100644
--- a/web/templates_templ.go
+++ b/web/templates_templ.go
@@ -58,7 +58,7 @@ func Layout(title string, content templ.Component) templ.Component {
})
}
-func RidesPage(items []RideView, headers []RideSortHeader) templ.Component {
+func RidesPage(data RidesPageData) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
@@ -79,7 +79,7 @@ func RidesPage(items []RideView, headers []RideSortHeader) templ.Component {
templ_7745c5c3_Var3 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Err = Layout("Rides", RidesContent(items, headers)).Render(ctx, templ_7745c5c3_Buffer)
+ templ_7745c5c3_Err = Layout("Rides", RidesContent(data)).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -87,7 +87,7 @@ func RidesPage(items []RideView, headers []RideSortHeader) templ.Component {
})
}
-func RidesContent(items []RideView, headers []RideSortHeader) templ.Component {
+func RidesContent(data RidesPageData) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
@@ -108,23 +108,579 @@ func RidesContent(items []RideView, headers []RideSortHeader) templ.Component {
templ_7745c5c3_Var4 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<p class=\"eyebrow\">Ride library</p><div class=\"toolbar\"><div><h1>All rides</h1><p class=\"lead\">Your imported rides, ready for climb analysis.</p></div><a class=\"button\" href=\"/sync\">Sync Strava</a></div><section class=\"panel\">")
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<p class=\"eyebrow\">Ride library</p><section class=\"panel ride-filters-panel\"><form id=\"ride-filters\" class=\"ride-filters\" method=\"get\" action=\"/\" aria-label=\"Ride filters\"><div class=\"section-heading\"><div><p class=\"eyebrow\">Refine</p><h2>Filter rides</h2></div><div class=\"ride-filter-actions\"><button class=\"button\" type=\"submit\">Apply filters</button><a class=\"button secondary\" href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- if len(items) == 0 {
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"empty\">No rides stored yet. <a href=\"/sync\">Import your first Strava rides.</a></div>")
+ var templ_7745c5c3_Var5 templ.SafeURL = templ.URL(data.Filters.ResetURL)
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var5)))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">Reset</a></div></div><input type=\"hidden\" name=\"sort\" value=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var6 string
+ templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(data.Sort.Column)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 37, Col: 60}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"> <input type=\"hidden\" name=\"dir\" value=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var7 string
+ templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(rideSortDirection(data.Sort))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 38, Col: 71}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><div class=\"ride-filter-ranges\"><fieldset class=\"ride-filter-group\"><legend>Distance</legend><div class=\"range-values\"><output data-range-value=\"min\" for=\"distance-min\">")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var8 string
+ templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideFilterDistance(data.Filters.DistanceMinKm))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 42, Col: 135}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</output><span aria-hidden=\"true\">to</span><output data-range-value=\"max\" for=\"distance-max\">")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var9 string
+ templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideFilterDistance(data.Filters.DistanceMaxKm))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 42, Col: 284}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</output></div><div class=\"range-control\" data-range-filter=\"distance\" data-range-format=\"distance\"><div class=\"range-track\" data-range-track></div><input id=\"distance-min\" class=\"range-input range-input-min\" type=\"range\" name=\"distance_min\" min=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var10 string
+ templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideFilterInt(rideFilterDistanceMinKm))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 45, Col: 150}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" max=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var11 string
+ templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideFilterInt(rideFilterDistanceMaxKm))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 45, Col: 203}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" step=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var12 string
+ templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideFilterInt(rideFilterDistanceStepKm))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 45, Col: 258}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" value=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var13 string
+ templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideFilterInt(data.Filters.DistanceMinKm))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 45, Col: 316}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" data-range-min aria-label=\"Minimum distance\"> <input id=\"distance-max\" class=\"range-input range-input-max\" type=\"range\" name=\"distance_max\" min=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var14 string
+ templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideFilterInt(rideFilterDistanceMinKm))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 46, Col: 150}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" max=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var15 string
+ templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideFilterInt(rideFilterDistanceMaxKm))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 46, Col: 203}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" step=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var16 string
+ templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideFilterInt(rideFilterDistanceStepKm))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 46, Col: 258}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" value=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var17 string
+ templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideFilterInt(data.Filters.DistanceMaxKm))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 46, Col: 316}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" data-range-max aria-label=\"Maximum distance\"></div></fieldset><fieldset class=\"ride-filter-group\"><legend>Moving duration</legend><div class=\"range-values\"><output data-range-value=\"min\" for=\"duration-min\">")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var18 string
+ templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideFilterDuration(data.Filters.DurationMinMinutes))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 51, Col: 140}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</output><span aria-hidden=\"true\">to</span><output data-range-value=\"max\" for=\"duration-max\">")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var19 string
+ templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideFilterDuration(data.Filters.DurationMaxMinutes))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 51, Col: 294}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</output></div><div class=\"range-control\" data-range-filter=\"duration\" data-range-format=\"duration\"><div class=\"range-track\" data-range-track></div><input id=\"duration-min\" class=\"range-input range-input-min\" type=\"range\" name=\"duration_min\" min=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var20 string
+ templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideFilterInt(rideFilterDurationMinMinutes))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 54, Col: 155}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" max=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var21 string
+ templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideFilterInt(rideFilterDurationMaxMinutes))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 54, Col: 213}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" step=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var22 string
+ templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideFilterInt(rideFilterDurationStepMinutes))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 54, Col: 273}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" value=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var23 string
+ templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideFilterInt(data.Filters.DurationMinMinutes))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 54, Col: 336}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" data-range-min aria-label=\"Minimum moving duration\"> <input id=\"duration-max\" class=\"range-input range-input-max\" type=\"range\" name=\"duration_max\" min=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var24 string
+ templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideFilterInt(rideFilterDurationMinMinutes))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 55, Col: 155}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" max=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var25 string
+ templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideFilterInt(rideFilterDurationMaxMinutes))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 55, Col: 213}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" step=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var26 string
+ templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideFilterInt(rideFilterDurationStepMinutes))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 55, Col: 273}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" value=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var27 string
+ templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideFilterInt(data.Filters.DurationMaxMinutes))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 55, Col: 336}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" data-range-max aria-label=\"Maximum moving duration\"></div></fieldset><fieldset class=\"ride-filter-group\"><legend>Cotacol</legend><div class=\"range-values\"><output data-range-value=\"min\" for=\"cotacol-min\">")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var28 string
+ templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideFilterCotacol(data.Filters.CotacolMinPoints))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 60, Col: 136}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</output><span aria-hidden=\"true\">to</span><output data-range-value=\"max\" for=\"cotacol-max\">")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var29 string
+ templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideFilterCotacol(data.Filters.CotacolMaxPoints))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 60, Col: 286}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</output></div><div class=\"range-control\" data-range-filter=\"cotacol\" data-range-format=\"cotacol\"><div class=\"range-track\" data-range-track></div><input id=\"cotacol-min\" class=\"range-input range-input-min\" type=\"range\" name=\"cotacol_min\" min=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var30 string
+ templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideFilterInt(rideFilterCotacolMinPoints))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 63, Col: 151}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" max=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var31 string
+ templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideFilterInt(rideFilterCotacolMaxPoints))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 63, Col: 207}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" step=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var32 string
+ templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideFilterInt(rideFilterCotacolStepPoints))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 63, Col: 265}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" value=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var33 string
+ templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideFilterInt(data.Filters.CotacolMinPoints))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 63, Col: 326}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" data-range-min aria-label=\"Minimum Cotacol\"> <input id=\"cotacol-max\" class=\"range-input range-input-max\" type=\"range\" name=\"cotacol_max\" min=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var34 string
+ templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideFilterInt(rideFilterCotacolMinPoints))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 64, Col: 151}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" max=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var35 string
+ templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideFilterInt(rideFilterCotacolMaxPoints))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 64, Col: 207}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" step=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var36 string
+ templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideFilterInt(rideFilterCotacolStepPoints))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 64, Col: 265}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var36))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" value=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var37 string
+ templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideFilterInt(data.Filters.CotacolMaxPoints))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 64, Col: 326}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var37))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" data-range-max aria-label=\"Maximum Cotacol\"></div></fieldset></div><div class=\"ride-filter-meta\"><fieldset class=\"ride-filter-group date-filter-group\"><legend>Started</legend><div class=\"date-filter-inputs\"><label for=\"ride-date-from\">From<input id=\"ride-date-from\" type=\"date\" name=\"date_from\" value=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var38 string
+ templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.JoinStringErrs(data.Filters.DateFrom)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 71, Col: 154}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var38))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"></label><label for=\"ride-date-to\">To<input id=\"ride-date-to\" type=\"date\" name=\"date_to\" value=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var39 string
+ templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinStringErrs(data.Filters.DateTo)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 71, Col: 273}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"></label></div></fieldset><label class=\"route-filter\" for=\"ride-route\">Route <select id=\"ride-route\" name=\"route\">")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ if data.Filters.Route == rideRouteFilterAny {
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<option value=\"\" selected>Any route</option> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<option value=\"\">Any route</option> ")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ }
+ if data.Filters.Route == rideRouteFilterAvailable {
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<option value=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var40 string
+ templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(rideRouteFilterAvailable)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 81, Col: 47}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" selected>Recorded route</option> ")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ } else {
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<option value=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var41 string
+ templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(rideRouteFilterAvailable)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 83, Col: 47}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">Recorded route</option> ")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ }
+ if data.Filters.Route == rideRouteFilterMissing {
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<option value=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var42 string
+ templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinStringErrs(rideRouteFilterMissing)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 86, Col: 45}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" selected>Metadata only</option>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ } else {
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<option value=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var43 string
+ templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs(rideRouteFilterMissing)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 88, Col: 45}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">Metadata only</option>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</select></label></div></form></section><section class=\"panel\">")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ if len(data.Items) == 0 {
+ if data.Filters.Active {
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"empty\">No rides match the selected filters. <a href=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var44 templ.SafeURL = templ.URL(data.Filters.ResetURL)
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var44)))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">Clear filters.</a></div>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ } else {
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"empty\">No rides stored yet. <a href=\"/sync\">Import your first Strava rides.</a></div>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ }
+ } else {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<table><thead><tr>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- for _, header := range headers {
- var templ_7745c5c3_Var5 = []any{header.Class}
- templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var5...)
+ for _, header := range data.Headers {
+ var templ_7745c5c3_Var45 = []any{header.Class}
+ templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var45...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -132,12 +688,12 @@ func RidesContent(items []RideView, headers []RideSortHeader) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var6 string
- templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var5).String())
+ var templ_7745c5c3_Var46 string
+ templ_7745c5c3_Var46, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var45).String())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 1, Col: 0}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var46))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -145,12 +701,12 @@ func RidesContent(items []RideView, headers []RideSortHeader) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var7 string
- templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(header.AriaSort)
+ var templ_7745c5c3_Var47 string
+ templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.JoinStringErrs(header.AriaSort)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 42, Col: 72}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 106, Col: 72}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var47))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -158,8 +714,8 @@ func RidesContent(items []RideView, headers []RideSortHeader) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var8 templ.SafeURL = templ.URL(header.URL)
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var8)))
+ var templ_7745c5c3_Var48 templ.SafeURL = templ.URL(header.URL)
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var48)))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -167,12 +723,12 @@ func RidesContent(items []RideView, headers []RideSortHeader) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var9 string
- templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(header.Label)
+ var templ_7745c5c3_Var49 string
+ templ_7745c5c3_Var49, templ_7745c5c3_Err = templ.JoinStringErrs(header.Label)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 42, Col: 141}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 106, Col: 141}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var49))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -180,12 +736,12 @@ func RidesContent(items []RideView, headers []RideSortHeader) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var10 string
- templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(header.Indicator)
+ var templ_7745c5c3_Var50 string
+ templ_7745c5c3_Var50, templ_7745c5c3_Err = templ.JoinStringErrs(header.Indicator)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 42, Col: 209}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 106, Col: 209}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var50))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -198,13 +754,13 @@ func RidesContent(items []RideView, headers []RideSortHeader) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- for _, item := range items {
+ for _, item := range data.Items {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<tr><td><strong><a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var11 templ.SafeURL = templ.URL(rideDetailURL(item.ID))
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var11)))
+ var templ_7745c5c3_Var51 templ.SafeURL = templ.URL(rideDetailURL(item.ID))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var51)))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -212,12 +768,12 @@ func RidesContent(items []RideView, headers []RideSortHeader) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var12 string
- templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(item.Name)
+ var templ_7745c5c3_Var52 string
+ templ_7745c5c3_Var52, templ_7745c5c3_Err = templ.JoinStringErrs(item.Name)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 47, Col: 79}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 111, Col: 79}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var52))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -225,12 +781,12 @@ func RidesContent(items []RideView, headers []RideSortHeader) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var13 string
- templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(item.Type)
+ var templ_7745c5c3_Var53 string
+ templ_7745c5c3_Var53, templ_7745c5c3_Err = templ.JoinStringErrs(item.Type)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 47, Col: 117}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 111, Col: 117}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var53))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -238,12 +794,12 @@ func RidesContent(items []RideView, headers []RideSortHeader) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var14 string
- templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideDate(item.StartDate))
+ var templ_7745c5c3_Var54 string
+ templ_7745c5c3_Var54, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideDate(item.StartDate))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 47, Col: 168}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 111, Col: 168}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var54))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -251,12 +807,12 @@ func RidesContent(items []RideView, headers []RideSortHeader) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var15 string
- templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(formatDistance(item.DistanceM))
+ var templ_7745c5c3_Var55 string
+ templ_7745c5c3_Var55, templ_7745c5c3_Err = templ.JoinStringErrs(formatDistance(item.DistanceM))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 47, Col: 227}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 111, Col: 227}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var55))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -264,12 +820,12 @@ func RidesContent(items []RideView, headers []RideSortHeader) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var16 string
- templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(formatDuration(item.MovingTimeS))
+ var templ_7745c5c3_Var56 string
+ templ_7745c5c3_Var56, templ_7745c5c3_Err = templ.JoinStringErrs(formatDuration(item.MovingTimeS))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 47, Col: 288}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 111, Col: 288}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var56))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -277,12 +833,12 @@ func RidesContent(items []RideView, headers []RideSortHeader) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var17 string
- templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(formatElevation(item.TotalElevationGainM))
+ var templ_7745c5c3_Var57 string
+ templ_7745c5c3_Var57, templ_7745c5c3_Err = templ.JoinStringErrs(formatElevation(item.TotalElevationGainM))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 47, Col: 358}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 111, Col: 358}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var57))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -290,12 +846,12 @@ func RidesContent(items []RideView, headers []RideSortHeader) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var18 string
- templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(item.Cotacol)
+ var templ_7745c5c3_Var58 string
+ templ_7745c5c3_Var58, templ_7745c5c3_Err = templ.JoinStringErrs(item.Cotacol)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 47, Col: 399}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 111, Col: 399}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var58))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -303,12 +859,12 @@ func RidesContent(items []RideView, headers []RideSortHeader) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var19 string
- templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(item.CotacolPer100Km)
+ var templ_7745c5c3_Var59 string
+ templ_7745c5c3_Var59, templ_7745c5c3_Err = templ.JoinStringErrs(item.CotacolPer100Km)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 47, Col: 448}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 111, Col: 448}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var59))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -322,7 +878,7 @@ func RidesContent(items []RideView, headers []RideSortHeader) templ.Component {
return templ_7745c5c3_Err
}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</section>")
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</section><script type=\"module\" src=\"/static/ride-filters.js\"></script>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -346,9 +902,9 @@ func RideDetailPage(data RideDetailView) templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var20 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var20 == nil {
- templ_7745c5c3_Var20 = templ.NopComponent
+ templ_7745c5c3_Var60 := templ.GetChildren(ctx)
+ if templ_7745c5c3_Var60 == nil {
+ templ_7745c5c3_Var60 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = Layout("Ride detail", RideDetailContent(data)).Render(ctx, templ_7745c5c3_Buffer)
@@ -375,21 +931,21 @@ func RideDetailContent(data RideDetailView) templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var21 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var21 == nil {
- templ_7745c5c3_Var21 = templ.NopComponent
+ templ_7745c5c3_Var61 := templ.GetChildren(ctx)
+ if templ_7745c5c3_Var61 == nil {
+ templ_7745c5c3_Var61 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"toolbar\"><div><h1 class=\"ride-detail-title\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var22 string
- templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(data.Ride.Name)
+ var templ_7745c5c3_Var62 string
+ templ_7745c5c3_Var62, templ_7745c5c3_Err = templ.JoinStringErrs(data.Ride.Name)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 61, Col: 53}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 126, Col: 53}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var62))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -397,12 +953,12 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var23 string
- templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(data.Ride.Type)
+ var templ_7745c5c3_Var63 string
+ templ_7745c5c3_Var63, templ_7745c5c3_Err = templ.JoinStringErrs(data.Ride.Type)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 61, Col: 92}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 126, Col: 92}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var63))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -410,12 +966,12 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var24 string
- templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideDate(data.Ride.StartDate))
+ var templ_7745c5c3_Var64 string
+ templ_7745c5c3_Var64, templ_7745c5c3_Err = templ.JoinStringErrs(formatRideDate(data.Ride.StartDate))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 61, Col: 135}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 126, Col: 135}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var64))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -428,12 +984,12 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var25 string
- templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(data.RouteError)
+ var templ_7745c5c3_Var65 string
+ templ_7745c5c3_Var65, templ_7745c5c3_Err = templ.JoinStringErrs(data.RouteError)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 65, Col: 38}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 130, Col: 38}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var65))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -447,12 +1003,12 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var26 string
- templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(data.Notice)
+ var templ_7745c5c3_Var66 string
+ templ_7745c5c3_Var66, templ_7745c5c3_Err = templ.JoinStringErrs(data.Notice)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 68, Col: 35}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 133, Col: 35}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var66))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -466,12 +1022,12 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var27 string
- templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(data.ActionError)
+ var templ_7745c5c3_Var67 string
+ templ_7745c5c3_Var67, templ_7745c5c3_Err = templ.JoinStringErrs(data.ActionError)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 71, Col: 39}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 136, Col: 39}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var67))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -484,12 +1040,12 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var28 string
- templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(formatDistance(data.Ride.DistanceM))
+ var templ_7745c5c3_Var68 string
+ templ_7745c5c3_Var68, templ_7745c5c3_Err = templ.JoinStringErrs(formatDistance(data.Ride.DistanceM))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 76, Col: 67}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 141, Col: 67}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var68))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -497,12 +1053,12 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var29 string
- templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(formatDuration(data.Ride.MovingTimeS))
+ var templ_7745c5c3_Var69 string
+ templ_7745c5c3_Var69, templ_7745c5c3_Err = templ.JoinStringErrs(formatDuration(data.Ride.MovingTimeS))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 77, Col: 72}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 142, Col: 72}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var69))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -510,12 +1066,12 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var30 string
- templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(formatElevation(data.Ride.TotalElevationGainM))
+ var templ_7745c5c3_Var70 string
+ templ_7745c5c3_Var70, templ_7745c5c3_Err = templ.JoinStringErrs(formatElevation(data.Ride.TotalElevationGainM))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 78, Col: 79}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 143, Col: 79}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var70))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -534,12 +1090,12 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var31 string
- templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(formatClimbNumber(climb.Index))
+ var templ_7745c5c3_Var71 string
+ templ_7745c5c3_Var71, templ_7745c5c3_Err = templ.JoinStringErrs(formatClimbNumber(climb.Index))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 91, Col: 114}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 156, Col: 114}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var71))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -547,12 +1103,12 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var32 string
- templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(climb.OfficialName)
+ var templ_7745c5c3_Var72 string
+ templ_7745c5c3_Var72, templ_7745c5c3_Err = templ.JoinStringErrs(climb.OfficialName)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 91, Col: 151}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 156, Col: 151}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var72))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -560,12 +1116,12 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var33 string
- templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(formatProfileDistance(climb.DistanceKm))
+ var templ_7745c5c3_Var73 string
+ templ_7745c5c3_Var73, templ_7745c5c3_Err = templ.JoinStringErrs(formatProfileDistance(climb.DistanceKm))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 93, Col: 75}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 158, Col: 75}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var73))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -573,12 +1129,12 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var34 string
- templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(formatCotacol(climb.Cotacol))
+ var templ_7745c5c3_Var74 string
+ templ_7745c5c3_Var74, templ_7745c5c3_Err = templ.JoinStringErrs(formatCotacol(climb.Cotacol))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 94, Col: 65}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 159, Col: 65}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var74))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -586,12 +1142,12 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var35 string
- templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(formatSlope(climb.SlopePercent))
+ var templ_7745c5c3_Var75 string
+ templ_7745c5c3_Var75, templ_7745c5c3_Err = templ.JoinStringErrs(formatSlope(climb.SlopePercent))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 95, Col: 70}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 160, Col: 70}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var75))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -599,12 +1155,12 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var36 string
- templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(formatRouteIndex(climb.StartIndex))
+ var templ_7745c5c3_Var76 string
+ templ_7745c5c3_Var76, templ_7745c5c3_Err = templ.JoinStringErrs(formatRouteIndex(climb.StartIndex))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 100, Col: 125}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 165, Col: 125}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var36))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var76))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -612,12 +1168,12 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var37 string
- templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.JoinStringErrs(formatRouteIndex(climb.EndIndex))
+ var templ_7745c5c3_Var77 string
+ templ_7745c5c3_Var77, templ_7745c5c3_Err = templ.JoinStringErrs(formatRouteIndex(climb.EndIndex))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 100, Col: 179}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 165, Col: 179}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var37))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var77))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -625,12 +1181,12 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var38 string
- templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.JoinStringErrs(officialProfileLabel(climb.OfficialName))
+ var templ_7745c5c3_Var78 string
+ templ_7745c5c3_Var78, templ_7745c5c3_Err = templ.JoinStringErrs(officialProfileLabel(climb.OfficialName))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 100, Col: 259}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 165, Col: 259}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var38))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var78))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -648,8 +1204,8 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var39 = []any{rideDetailColumnsClass(len(data.Profile.UnmatchedClimbs()))}
- templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var39...)
+ var templ_7745c5c3_Var79 = []any{rideDetailColumnsClass(len(data.Profile.UnmatchedClimbs()))}
+ templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var79...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -657,12 +1213,12 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var40 string
- templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var39).String())
+ var templ_7745c5c3_Var80 string
+ templ_7745c5c3_Var80, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var79).String())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 1, Col: 0}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var80))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -675,12 +1231,12 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var41 string
- templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(formatClimbCount(len(data.Profile.UnmatchedClimbs())))
+ var templ_7745c5c3_Var81 string
+ templ_7745c5c3_Var81, templ_7745c5c3_Err = templ.JoinStringErrs(formatClimbCount(len(data.Profile.UnmatchedClimbs())))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 119, Col: 89}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 184, Col: 89}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var81))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -688,12 +1244,12 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var42 string
- templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinStringErrs(formatClimbCount(len(data.Profile.UnmatchedClimbs())))
+ var templ_7745c5c3_Var82 string
+ templ_7745c5c3_Var82, templ_7745c5c3_Err = templ.JoinStringErrs(formatClimbCount(len(data.Profile.UnmatchedClimbs())))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 123, Col: 100}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 188, Col: 100}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var82))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -706,12 +1262,12 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var43 string
- templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs(formatRouteIndex(climb.Index))
+ var templ_7745c5c3_Var83 string
+ templ_7745c5c3_Var83, templ_7745c5c3_Err = templ.JoinStringErrs(formatRouteIndex(climb.Index))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 128, Col: 99}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 193, Col: 99}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var83))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -719,12 +1275,12 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var44 string
- templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.JoinStringErrs(formatClimbNumber(climb.Index))
+ var templ_7745c5c3_Var84 string
+ templ_7745c5c3_Var84, templ_7745c5c3_Err = templ.JoinStringErrs(formatClimbNumber(climb.Index))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 130, Col: 82}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 195, Col: 82}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var44))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var84))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -732,12 +1288,12 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var45 string
- templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinStringErrs(climb.Name)
+ var templ_7745c5c3_Var85 string
+ templ_7745c5c3_Var85, templ_7745c5c3_Err = templ.JoinStringErrs(climb.Name)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 130, Col: 103}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 195, Col: 103}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var85))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -745,12 +1301,12 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var46 string
- templ_7745c5c3_Var46, templ_7745c5c3_Err = templ.JoinStringErrs(formatProfileDistance(climb.StartKm))
+ var templ_7745c5c3_Var86 string
+ templ_7745c5c3_Var86, templ_7745c5c3_Err = templ.JoinStringErrs(formatProfileDistance(climb.StartKm))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 131, Col: 92}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 196, Col: 92}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var46))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var86))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -758,12 +1314,12 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var47 string
- templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.JoinStringErrs(formatProfileDistance(climb.EndKm))
+ var templ_7745c5c3_Var87 string
+ templ_7745c5c3_Var87, templ_7745c5c3_Err = templ.JoinStringErrs(formatProfileDistance(climb.EndKm))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 131, Col: 133}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 196, Col: 133}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var47))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var87))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -771,12 +1327,12 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var48 string
- templ_7745c5c3_Var48, templ_7745c5c3_Err = templ.JoinStringErrs(climb.Category)
+ var templ_7745c5c3_Var88 string
+ templ_7745c5c3_Var88, templ_7745c5c3_Err = templ.JoinStringErrs(climb.Category)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 133, Col: 68}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 198, Col: 68}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var48))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var88))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -784,12 +1340,12 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var49 string
- templ_7745c5c3_Var49, templ_7745c5c3_Err = templ.JoinStringErrs(formatProfileDistance(climb.DistanceKm))
+ var templ_7745c5c3_Var89 string
+ templ_7745c5c3_Var89, templ_7745c5c3_Err = templ.JoinStringErrs(formatProfileDistance(climb.DistanceKm))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 133, Col: 115}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 198, Col: 115}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var49))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var89))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -797,12 +1353,12 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var50 string
- templ_7745c5c3_Var50, templ_7745c5c3_Err = templ.JoinStringErrs(formatSlope(climb.SlopePercent))
+ var templ_7745c5c3_Var90 string
+ templ_7745c5c3_Var90, templ_7745c5c3_Err = templ.JoinStringErrs(formatSlope(climb.SlopePercent))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 133, Col: 154}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 198, Col: 154}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var50))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var90))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -810,12 +1366,12 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var51 string
- templ_7745c5c3_Var51, templ_7745c5c3_Err = templ.JoinStringErrs(formatCotacol(climb.Cotacol))
+ var templ_7745c5c3_Var91 string
+ templ_7745c5c3_Var91, templ_7745c5c3_Err = templ.JoinStringErrs(formatCotacol(climb.Cotacol))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 133, Col: 198}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 198, Col: 198}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var51))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var91))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -823,8 +1379,8 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var52 templ.SafeURL = templ.URL(officialClimbCreateURL(data.Ride.ID))
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var52)))
+ var templ_7745c5c3_Var92 templ.SafeURL = templ.URL(officialClimbCreateURL(data.Ride.ID))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var92)))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -832,12 +1388,12 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var53 string
- templ_7745c5c3_Var53, templ_7745c5c3_Err = templ.JoinStringErrs(formatRouteIndex(climb.StartIndex))
+ var templ_7745c5c3_Var93 string
+ templ_7745c5c3_Var93, templ_7745c5c3_Err = templ.JoinStringErrs(formatRouteIndex(climb.StartIndex))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 137, Col: 92}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 202, Col: 92}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var53))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var93))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -845,12 +1401,12 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var54 string
- templ_7745c5c3_Var54, templ_7745c5c3_Err = templ.JoinStringErrs(formatRouteIndex(climb.EndIndex))
+ var templ_7745c5c3_Var94 string
+ templ_7745c5c3_Var94, templ_7745c5c3_Err = templ.JoinStringErrs(formatRouteIndex(climb.EndIndex))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 138, Col: 87}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 203, Col: 87}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var54))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var94))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -858,12 +1414,12 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var55 string
- templ_7745c5c3_Var55, templ_7745c5c3_Err = templ.JoinStringErrs(formatProfileDistance(climb.StartKm))
+ var templ_7745c5c3_Var95 string
+ templ_7745c5c3_Var95, templ_7745c5c3_Err = templ.JoinStringErrs(formatProfileDistance(climb.StartKm))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 140, Col: 428}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 205, Col: 428}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var55))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var95))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -871,12 +1427,12 @@ func RideDetailContent(data RideDetailView) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var56 string
- templ_7745c5c3_Var56, templ_7745c5c3_Err = templ.JoinStringErrs(formatProfileDistance(climb.EndKm))
+ var templ_7745c5c3_Var96 string
+ templ_7745c5c3_Var96, templ_7745c5c3_Err = templ.JoinStringErrs(formatProfileDistance(climb.EndKm))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 141, Col: 414}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 206, Col: 414}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var56))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var96))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -945,9 +1501,9 @@ func SyncPage(data SyncPageData) templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var57 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var57 == nil {
- templ_7745c5c3_Var57 = templ.NopComponent
+ templ_7745c5c3_Var97 := templ.GetChildren(ctx)
+ if templ_7745c5c3_Var97 == nil {
+ templ_7745c5c3_Var97 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = Layout("Sync Strava", SyncContent(data)).Render(ctx, templ_7745c5c3_Buffer)
@@ -974,9 +1530,9 @@ func SyncContent(data SyncPageData) templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var58 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var58 == nil {
- templ_7745c5c3_Var58 = templ.NopComponent
+ templ_7745c5c3_Var98 := templ.GetChildren(ctx)
+ if templ_7745c5c3_Var98 == nil {
+ templ_7745c5c3_Var98 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<p class=\"eyebrow\">Data import</p><h1>Sync Strava rides</h1><p class=\"lead\">Choose a date range. New rides are downloaded as GPX files and recorded in the local library.</p>")
@@ -988,12 +1544,12 @@ func SyncContent(data SyncPageData) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var59 string
- templ_7745c5c3_Var59, templ_7745c5c3_Err = templ.JoinStringErrs(data.Notice)
+ var templ_7745c5c3_Var99 string
+ templ_7745c5c3_Var99, templ_7745c5c3_Err = templ.JoinStringErrs(data.Notice)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 187, Col: 52}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 252, Col: 52}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var59))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var99))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -1012,12 +1568,12 @@ func SyncContent(data SyncPageData) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var60 string
- templ_7745c5c3_Var60, templ_7745c5c3_Err = templ.JoinStringErrs(data.Error)
+ var templ_7745c5c3_Var100 string
+ templ_7745c5c3_Var100, templ_7745c5c3_Err = templ.JoinStringErrs(data.Error)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 192, Col: 49}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 257, Col: 49}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var60))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var100))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -1040,8 +1596,8 @@ func SyncContent(data SyncPageData) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var61 templ.SafeURL = templ.URL("/strava/login?return_to=/sync")
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var61)))
+ var templ_7745c5c3_Var101 templ.SafeURL = templ.URL("/strava/login?return_to=/sync")
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var101)))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -1054,12 +1610,12 @@ func SyncContent(data SyncPageData) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var62 string
- templ_7745c5c3_Var62, templ_7745c5c3_Err = templ.JoinStringErrs(data.From)
+ var templ_7745c5c3_Var102 string
+ templ_7745c5c3_Var102, templ_7745c5c3_Err = templ.JoinStringErrs(data.From)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 203, Col: 64}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 268, Col: 64}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var62))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var102))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -1067,12 +1623,12 @@ func SyncContent(data SyncPageData) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var63 string
- templ_7745c5c3_Var63, templ_7745c5c3_Err = templ.JoinStringErrs(data.To)
+ var templ_7745c5c3_Var103 string
+ templ_7745c5c3_Var103, templ_7745c5c3_Err = templ.JoinStringErrs(data.To)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 204, Col: 58}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates.templ`, Line: 269, Col: 58}
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var63))
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var103))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
diff --git a/web/views.go b/web/views.go
index b728de8..de4d38f 100644
--- a/web/views.go
+++ b/web/views.go
@@ -18,6 +18,13 @@ type RideView struct {
CotacolPer100Km string
}
+type RidesPageData struct {
+ Items []RideView
+ Headers []RideSortHeader
+ Filters RideFilterView
+ Sort RideSort
+}
+
type RideDetailView struct {
RideView
Route GeoJSONFeatureCollection
@@ -168,6 +175,10 @@ func (s RideSort) databaseColumn() (rides.SortColumn, bool) {
}
func rideSortHeaders(current RideSort) []RideSortHeader {
+ return rideSortHeadersWithFilters(current, RideFilters{})
+}
+
+func rideSortHeadersWithFilters(current RideSort, filters RideFilters) []RideSortHeader {
columns := []struct {
key string
label string
@@ -188,13 +199,9 @@ func rideSortHeaders(current RideSort) []RideSortHeader {
if active {
descending = !current.Descending
}
- query := url.Values{}
+ query := rideFilterQuery(filters)
query.Set("sort", column.key)
- if descending {
- query.Set("dir", "desc")
- } else {
- query.Set("dir", "asc")
- }
+ query.Set("dir", rideSortDirection(RideSort{Column: column.key, Descending: descending}))
headerClass := column.class
ariaSort := "none"
indicator := ""
@@ -219,6 +226,13 @@ func rideSortHeaders(current RideSort) []RideSortHeader {
return headers
}
+func rideSortDirection(sort RideSort) string {
+ if sort.Descending {
+ return "desc"
+ }
+ return "asc"
+}
+
func rideDetailURL(id int64) string {
return fmt.Sprintf("/rides/%d", id)
}