Dedup Garmin data storage, configurable chart colors, taxonomy fixes, sync/progression fixes
- Remove duplicated Garmin fields from storage; decode display-only fields (activity name/type, lap duration/HR, structured workout raw JSON) from RawJSON at API-response time instead of storing redundant columns - Add a fully configurable chart color system (pace/HR main-line colors, 4 effort-kind colors, tint/darken/brighten intensity knobs) under Profile > Chart colors - Rename training types and fix their display order (Easy, Long, 60'/30' Threshold, Tempo, Intervals, MAS Test, Race) everywhere they're listed - Add an Efficiency Factor progression metric; fix Progression chart axes to use tight non-zero-based domains, m:ss/km pace formatting, and rounded ticks instead of raw floating-point labels - Expose the raw get_workout_by_id() payload in the raw-data viewer alongside activity/lap/detail JSON; enlarge the modal and shrink array indentation for readability - Fix "last sync" reporting a meaningless activity count: record one combined sync run per manual "Sync now" and count genuinely new activities instead of re-listing whatever Garmin returned for the queried window - Let a Review Queue activity be manually cleared back to Unclassified, and make "Reset all" available even while disconnected from Garmin Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -18,8 +18,8 @@ func TestWorkoutTaxonomy_SeededWithEightFixedTypes(t *testing.T) {
|
||||
}
|
||||
|
||||
wantNames := map[string]bool{
|
||||
"Easy Run": false, "Long Run": false, "Threshold 30'": false, "Threshold 60'": false,
|
||||
"Tempo": false, "Interval": false, "MAS Test": false, "Race": false,
|
||||
"Easy": false, "Long": false, "60' Threshold": false, "30' Threshold": false,
|
||||
"Tempo": false, "Intervals": false, "MAS Test": false, "Race": false,
|
||||
}
|
||||
for _, k := range kinds {
|
||||
if _, ok := wantNames[k.Name]; !ok {
|
||||
@@ -34,3 +34,31 @@ func TestWorkoutTaxonomy_SeededWithEightFixedTypes(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Every list of training types (Activities filters, Progression's kind
|
||||
// picker, Profile's Training types card) relies on ListWorkoutKinds' own
|
||||
// ORDER BY priority DESC, name to already be in this exact fixed order --
|
||||
// none of them re-sort client-side.
|
||||
func TestWorkoutTaxonomy_ListedInFixedDisplayOrder(t *testing.T) {
|
||||
db := openTestDB(t)
|
||||
ctx := context.Background()
|
||||
|
||||
kinds, err := db.ListWorkoutKinds(ctx, true)
|
||||
if err != nil {
|
||||
t.Fatalf("ListWorkoutKinds: %v", err)
|
||||
}
|
||||
|
||||
want := []string{"Easy", "Long", "60' Threshold", "30' Threshold", "Tempo", "Intervals", "MAS Test", "Race"}
|
||||
if len(kinds) != len(want) {
|
||||
t.Fatalf("expected %d kinds, got %d", len(want), len(kinds))
|
||||
}
|
||||
for i, name := range want {
|
||||
if kinds[i].Name != name {
|
||||
got := make([]string, len(kinds))
|
||||
for j, k := range kinds {
|
||||
got[j] = k.Name
|
||||
}
|
||||
t.Fatalf("position %d: got %q, want %q (full order: %v)", i, kinds[i].Name, name, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user