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:
@@ -55,7 +55,7 @@ export const api = {
|
||||
getActivity: (id: number) =>
|
||||
request<{ activity: Activity; laps: unknown[]; assignment?: KindAssignment }>(`/api/activities/${id}`),
|
||||
|
||||
// Workout kinds -- fixed taxonomy, no create/delete
|
||||
// Training types -- fixed taxonomy, no create/delete
|
||||
listWorkoutKinds: (includeInactive = false) =>
|
||||
request<WorkoutKind[]>(`/api/workout-kinds/${includeInactive ? "?include_inactive=true" : ""}`),
|
||||
updateWorkoutKind: (
|
||||
@@ -69,7 +69,8 @@ export const api = {
|
||||
is_active?: boolean;
|
||||
pace_min_sec_per_km?: number | null;
|
||||
pace_max_sec_per_km?: number | null;
|
||||
expected_hr_zone?: number | null;
|
||||
hr_min_pct_hrr?: number | null;
|
||||
hr_max_pct_hrr?: number | null;
|
||||
},
|
||||
) => request<WorkoutKind>(`/api/workout-kinds/${id}`, { method: "PUT", body: JSON.stringify(body) }),
|
||||
|
||||
@@ -85,11 +86,15 @@ export const api = {
|
||||
// Review queue -- cursor-paginated: pass the previous page's next_cursor
|
||||
// as `before` to fetch the next one. Fetching every activity's laps and
|
||||
// per-second samples up front gets expensive once there are many, so the
|
||||
// frontend loads it incrementally (infinite scroll) instead of all at once.
|
||||
reviewQueue: (params?: { limit?: number; before?: string }) => {
|
||||
// frontend loads it incrementally (infinite scroll) instead of all at once
|
||||
// -- kindId/unclassified filter server-side so a filtered view stays
|
||||
// paginated too, instead of having to load the whole matching backlog.
|
||||
reviewQueue: (params?: { limit?: number; before?: string; kindId?: number; unclassified?: boolean }) => {
|
||||
const q = new URLSearchParams();
|
||||
if (params?.limit) q.set("limit", String(params.limit));
|
||||
if (params?.before) q.set("before", params.before);
|
||||
if (params?.kindId != null) q.set("kind_id", String(params.kindId));
|
||||
if (params?.unclassified) q.set("unclassified", "true");
|
||||
const qs = q.toString();
|
||||
return request<ReviewQueuePage>(`/api/review-queue/${qs ? `?${qs}` : ""}`);
|
||||
},
|
||||
@@ -98,6 +103,14 @@ export const api = {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ workout_kind_id: workoutKindId }),
|
||||
}),
|
||||
// Reverts a manual assignment back to rule_engine sourcing (same kind),
|
||||
// so a later reclassify pass is free to change it again.
|
||||
unlockReview: (activityId: number) =>
|
||||
request<{ status: string }>(`/api/review-queue/${activityId}/unlock`, { method: "POST" }),
|
||||
// Manually clears an activity's kind back to Unclassified (locks that
|
||||
// decision, same as resolveReview locks a specific kind).
|
||||
unassignReview: (activityId: number) =>
|
||||
request<{ status: string }>(`/api/review-queue/${activityId}/unassign`, { method: "POST" }),
|
||||
|
||||
// Progression
|
||||
progression: (kindId: number, metric: ProgressionMetric = "pace", from?: string, to?: string) => {
|
||||
|
||||
Reference in New Issue
Block a user