Skip to content

feat: improve workout management#137

Merged
isotronic merged 13 commits into
masterfrom
various-fixes
May 13, 2026
Merged

feat: improve workout management#137
isotronic merged 13 commits into
masterfrom
various-fixes

Conversation

@isotronic
Copy link
Copy Markdown
Owner

@isotronic isotronic commented May 13, 2026

Summary by Sourcery

Add deletion support and soft-delete filtering for completed workouts, improve workout session input handling, and surface weekly plan schedules in the UI.

New Features:

  • Allow users to delete completed workouts from the history details screen with confirmation and react-query integration.
  • Display a weekly plan schedule summary for plans, including per-day workout/rest indicators and days-per-week count.
  • Highlight the next recommended workout card on the home screen when appropriate.

Bug Fixes:

  • Prevent duplicate sets from being added when fetching a completed workout's details.
  • Ensure workout history queries exclude soft-deleted completed workouts.
  • Fix workout session weight and reps adjustments by handling increments directly in the session component instead of relying on removed store methods.

Enhancements:

  • Refine history details header layout and icon sizing for better alignment.
  • Remove unused navigation bar configuration from the app config.
  • Disable the home screen start button only when a workout is starting, not on rest days.

Summary by CodeRabbit

  • New Features

    • Weekly schedule display added to Plan overview.
    • Option to delete completed workouts from History (confirmation shown).
  • Improvements

    • Workout cards highlight priority items and “Start” now available on rest days.
    • More robust weight/reps input handling during sessions (better precision and empty/invalid input handling).
    • Deleted completed workouts are excluded from history and details.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 13, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 21bd9a4c-efd0-4b65-9fe0-d4704d18b791

📥 Commits

Reviewing files that changed from the base of the PR and between fc57c8d and fd7d484.

📒 Files selected for processing (1)
  • app/(app)/(tabs)/(stats)/history-details.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/(app)/(tabs)/(stats)/history-details.tsx

📝 Walkthrough

Walkthrough

Adds soft-delete for completed workouts and filters/dedupes query results, provides a delete mutation and history UI, refactors weight/reps state handling to a unified setter, introduces WeeklyScheduleDisplay with plan integration, tweaks home/start behaviour, and removes an Expo androidNavigationBar config.

Changes

Workout Management and Schedule Features

Layer / File(s) Summary
Completed workout deletion with persistence and filtering
utils/database.ts, hooks/useCompletedWorkoutsQuery.ts
Soft-delete completed workouts in a single exclusive transaction; deduplicate set rows when fetching a completed workout; and filter deleted rows from completed-workout queries.
Delete mutation hook and history details screen integration
hooks/useDeleteCompletedWorkoutMutation.ts, app/(app)/(tabs)/(stats)/history-details.tsx
Adds a React Query mutation that calls deleteCompletedWorkout(id) with cache invalidation and navigation; wires a trash icon and confirmation alert into History Details and adjusts header layout.
Weight and reps state management refactor
store/activeWorkoutStore.ts, app/(app)/(workout)/workout-session.tsx
Removes increment/decrement helpers and uses updateWeightAndReps; session handlers now parse/format/clamp values locally before updating the store.
Weekly schedule display component
components/WeeklyScheduleDisplay.tsx
New component rendering either an empty state or a 7-day grid; maps workout IDs to names and displays Rest or workout names per day with theme-aware styling.
Plan overview schedule integration
app/(app)/(tabs)/(plans)/overview.tsx
Fetches plan schedule entries via usePlanScheduleQuery and renders WeeklyScheduleDisplay alongside plan workouts.
Home screen start workout on rest days
app/(app)/(tabs)/index.tsx
Uses shouldHighlightCard to conditionally style the first workout card; removes isRestDay from the start-button guard so Start can be triggered on rest days; disables button only while starting.
Android navigation bar config removal
app.config.js
Removes top-level androidNavigationBar configuration from the exported Expo config.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • isotronic/MuscleQuest#135: Introduces plan schedule data and related schedule UI that this PR consumes for the weekly schedule display.

Poem

🐰 A rabbit's note on the PR:

I hop and tidy workout tracks anew,
Soft-delete the past, so history stays true.
Reps and weights now parse with gentle art,
Weekly tiles show plans from end to start.
New starts on rest days — leap, then depart!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'feat: improve workout management' is overly broad and vague, covering multiple distinct changes including deletion, filtering, UI improvements, and store refactoring without highlighting the primary or most significant change. Consider a more specific title that captures the main focus, such as 'feat: add workout deletion and improve session input handling' or break into multiple commits with more targeted titles.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've reviewed this pull request using the Sourcery rules engine

@isotronic isotronic changed the title feat: fix workout session and enhance workout management with deletion, styling, and schedule display feat: enhance workout management with deletion, styling, and schedule display May 13, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
components/WeeklyScheduleDisplay.tsx (1)

38-44: ⚡ Quick win

Consider memoising the workout lookup for performance.

The workout-by-ID Map and schedule map are rebuilt on every render. Wrapping this logic in useMemo would avoid unnecessary recalculations when workouts and scheduleEntries haven't changed.

♻️ Proposed refactor with useMemo
+import React, { useMemo } from "react";
...
-  // Build day_of_week → workout name lookup
-  const workoutById = new Map(workouts.map((w) => [w.id, w]));
-  const scheduleMap: Record<number, string> = {};
-  for (const entry of scheduleEntries) {
-    const workout = workoutById.get(entry.workout_id);
-    scheduleMap[entry.day_of_week] = workout?.name || "Workout";
-  }
+  const scheduleMap = useMemo(() => {
+    const workoutById = new Map(workouts.map((w) => [w.id, w]));
+    const map: Record<number, string> = {};
+    for (const entry of scheduleEntries) {
+      const workout = workoutById.get(entry.workout_id);
+      map[entry.day_of_week] = workout?.name || "Workout";
+    }
+    return map;
+  }, [workouts, scheduleEntries]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/WeeklyScheduleDisplay.tsx` around lines 38 - 44, Wrap the
computation of workoutById and scheduleMap in a useMemo so they are only
recomputed when inputs change: compute workoutById = new Map(workouts.map(...))
and scheduleMap from scheduleEntries inside a single useMemo with [workouts,
scheduleEntries] as dependencies (preserve the current shapes/types and the
fallback "Workout" for missing names); then replace the existing top-level
consts with the memoized values so renders reuse the maps when workouts and
scheduleEntries are unchanged.
app/(app)/(tabs)/index.tsx (1)

342-354: ⚡ Quick win

Extract the repeated highlight condition to a named boolean.

The same complex condition appears in both the card border styling (lines 344-348) and the button mode logic (lines 389-393). This duplication increases maintenance burden and the risk of inconsistent updates.

♻️ Proposed refactor

Extract the condition to a named boolean immediately before the card rendering (around line 340):

               );
             });

+            const shouldHighlightFirstWorkout =
+              !workoutInProgress &&
+              index === 0 &&
+              !weeklyGoalReached &&
+              !isRestDay &&
+              completedTodayWorkoutIds.size === 0;
+
             return (
               <Pressable
                 key={index}
                 style={[
                   styles.workoutCard,
-                  !workoutInProgress &&
-                  index === 0 &&
-                  !weeklyGoalReached &&
-                  !isRestDay &&
-                  completedTodayWorkoutIds.size === 0
+                  shouldHighlightFirstWorkout
                     ? {
                         borderWidth: 1,
                         borderColor: Colors.dark.tint,
                       }
                     : null,
                 ]}

Then update the button mode:

                 <Button
                   mode={
-                    !workoutInProgress &&
-                    index === 0 &&
-                    !weeklyGoalReached &&
-                    !isRestDay &&
-                    completedTodayWorkoutIds.size === 0
+                    shouldHighlightFirstWorkout
                       ? "contained"
                       : "outlined"
                   }

Also applies to: 388-395

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`(app)/(tabs)/index.tsx around lines 342 - 354, Extract the repeated
complex condition into a named boolean (e.g., shouldHighlightCard) defined
immediately before the card render using the existing variables
workoutInProgress, index, weeklyGoalReached, isRestDay, and
completedTodayWorkoutIds; then replace the inline expression used in the style
array (previously applied to styles.workoutCard) and the duplicated button mode
logic with this new boolean so both places use shouldHighlightCard consistently
and remain a boolean value.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/`(app)/(tabs)/(stats)/history-details.tsx:
- Around line 151-163: The onPressIn handler calls
deleteMutation.mutate(Number(id)) without validating id; parse and validate the
route param (id) before invoking deleteMutation.mutate: use const parsedId =
Number(id) or parseInt(id, 10) and check Number.isFinite(parsedId) &&
Number.isInteger(parsedId) (and optionally parsedId > 0); if validation fails,
show an Alert (or no-op) instead of calling deleteMutation.mutate; update the
onPressIn block that contains deleteMutation.mutate(Number(id)) to perform this
guard and only call deleteMutation.mutate(parsedId) when valid.

In `@components/WeeklyScheduleDisplay.tsx`:
- Around line 10-13: The code builds a Map from WorkoutRef entries that may have
id=null/undefined, causing lookups by numeric schedule.workout_id to fail;
before constructing the Map in WeeklyScheduleDisplay, filter the workouts array
(the data used to build workoutMap) to only include entries where workout.id is
a valid number (e.g., id !== null && id !== undefined), then build the Map from
that filtered array so lookups by numeric workout_id succeed; optionally tighten
the WorkoutRef usage by asserting or narrowing id to number when inserting into
the Map and when consuming the Map for schedule rendering.

In `@utils/database.ts`:
- Around line 885-903: The code that builds sets in exercisesMap currently uses
"weight: convertedWeight || null" which coerces a valid 0 into null; in the
block that pushes into exercisesMap[row.exercise_id].sets (around the
convertedWeight calculation), replace that expression with a check that
preserves 0 and only sets null for non-numeric values, e.g. use
Number.isFinite(convertedWeight) ? convertedWeight : null (or an explicit !==
null/!== undefined and !Number.isNaN check) so legitimate zero weights remain 0.
- Around line 1337-1356: The soft-delete in deleteCompletedWorkout sets
is_deleted flags but fetchCompletedWorkoutById still returns rows by cw.id;
update fetchCompletedWorkoutById to ignore soft-deleted records by adding
is_deleted = FALSE (or = 0) filters to the completed_workouts query and any
joined/subqueries that read completed_exercises or completed_sets so deleted
workouts (and their child rows) are excluded when fetching by id; ensure the
function (fetchCompletedWorkoutById) and any helper queries use these guards
consistently.

---

Nitpick comments:
In `@app/`(app)/(tabs)/index.tsx:
- Around line 342-354: Extract the repeated complex condition into a named
boolean (e.g., shouldHighlightCard) defined immediately before the card render
using the existing variables workoutInProgress, index, weeklyGoalReached,
isRestDay, and completedTodayWorkoutIds; then replace the inline expression used
in the style array (previously applied to styles.workoutCard) and the duplicated
button mode logic with this new boolean so both places use shouldHighlightCard
consistently and remain a boolean value.

In `@components/WeeklyScheduleDisplay.tsx`:
- Around line 38-44: Wrap the computation of workoutById and scheduleMap in a
useMemo so they are only recomputed when inputs change: compute workoutById =
new Map(workouts.map(...)) and scheduleMap from scheduleEntries inside a single
useMemo with [workouts, scheduleEntries] as dependencies (preserve the current
shapes/types and the fallback "Workout" for missing names); then replace the
existing top-level consts with the memoized values so renders reuse the maps
when workouts and scheduleEntries are unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 8f64d2e5-fea2-4ec7-a400-8702d5f83e2d

📥 Commits

Reviewing files that changed from the base of the PR and between 3acd62f and 619a1ff.

📒 Files selected for processing (10)
  • app.config.js
  • app/(app)/(tabs)/(plans)/overview.tsx
  • app/(app)/(tabs)/(stats)/history-details.tsx
  • app/(app)/(tabs)/index.tsx
  • app/(app)/(workout)/workout-session.tsx
  • components/WeeklyScheduleDisplay.tsx
  • hooks/useCompletedWorkoutsQuery.ts
  • hooks/useDeleteCompletedWorkoutMutation.ts
  • store/activeWorkoutStore.ts
  • utils/database.ts
💤 Files with no reviewable changes (2)
  • app.config.js
  • store/activeWorkoutStore.ts

Comment thread app/(app)/(tabs)/(stats)/history-details.tsx Outdated
Comment thread components/WeeklyScheduleDisplay.tsx
Comment thread utils/database.ts
Comment thread utils/database.ts
@isotronic isotronic changed the title feat: enhance workout management with deletion, styling, and schedule display feat: improve workout management May 13, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
app/(app)/(tabs)/(stats)/history-details.tsx (1)

152-158: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use strict ID parsing to prevent deleting the wrong workout.

The id route parameter from useLocalSearchParams() has type string | string[] | undefined, which is not strictly validated. Line 152 uses parseInt(String(id), 10), which is permissive: it returns 12 from inputs like "12abc" or from arrays like ["12abc"]. The subsequent guard checks (finite, integer, positive) would all pass, allowing a malformed parameter to target the wrong record for deletion. Use strict parsing with a regex check on the raw string before conversion to ensure only valid numeric IDs proceed to the mutation.

Suggested fix
-                  const parsedId = parseInt(String(id), 10);
-                  if (
-                    !Number.isFinite(parsedId) ||
-                    !Number.isInteger(parsedId) ||
-                    parsedId <= 0
-                  )
-                    return;
+                  const rawId = Array.isArray(id) ? id[0] : id;
+                  const parsedId = Number(rawId);
+                  const isValidId =
+                    typeof rawId === "string" &&
+                    /^\d+$/.test(rawId) &&
+                    Number.isInteger(parsedId) &&
+                    parsedId > 0;
+                  if (!isValidId) {
+                    Alert.alert("Error", "Invalid workout ID.");
+                    return;
+                  }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`(app)/(tabs)/(stats)/history-details.tsx around lines 152 - 158, The
route param `id` from useLocalSearchParams() is being loosely parsed via
parseInt(String(id), 10) (see parsedId) which allows inputs like "12abc" or
arrays to slip through; change the guard to first ensure id is a single string
(typeof id === 'string'), test it against a strict digits-only regex (e.g.
/^\d+$/) and only then call parseInt to produce parsedId, returning early for
any non-string, array, or non-matching values so the subsequent
Number.isFinite/Integer/positive checks are redundant and cannot act on
malformed input.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@app/`(app)/(tabs)/(stats)/history-details.tsx:
- Around line 152-158: The route param `id` from useLocalSearchParams() is being
loosely parsed via parseInt(String(id), 10) (see parsedId) which allows inputs
like "12abc" or arrays to slip through; change the guard to first ensure id is a
single string (typeof id === 'string'), test it against a strict digits-only
regex (e.g. /^\d+$/) and only then call parseInt to produce parsedId, returning
early for any non-string, array, or non-matching values so the subsequent
Number.isFinite/Integer/positive checks are redundant and cannot act on
malformed input.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 3a09ce04-0d44-4e53-8c60-95abf79ec9fe

📥 Commits

Reviewing files that changed from the base of the PR and between 619a1ff and fc57c8d.

📒 Files selected for processing (4)
  • app/(app)/(tabs)/(stats)/history-details.tsx
  • app/(app)/(tabs)/index.tsx
  • components/WeeklyScheduleDisplay.tsx
  • utils/database.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • utils/database.ts
  • app/(app)/(tabs)/index.tsx
  • components/WeeklyScheduleDisplay.tsx

@isotronic isotronic merged commit 8539636 into master May 13, 2026
3 checks passed
@isotronic isotronic deleted the various-fixes branch May 13, 2026 06:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant