Skip to content

fix: add submission guard and loading state to therapy save, deduplicate regression reports#222

Open
Varadraj75 wants to merge 1 commit into
AOSSIE-Org:mainfrom
Varadraj75:fix/therapy-save-duplicate-submission-guard
Open

fix: add submission guard and loading state to therapy save, deduplicate regression reports#222
Varadraj75 wants to merge 1 commit into
AOSSIE-Org:mainfrom
Varadraj75:fix/therapy-save-duplicate-submission-guard

Conversation

@Varadraj75
Copy link
Copy Markdown
Contributor

@Varadraj75 Varadraj75 commented Apr 19, 2026

Closes #221

📝 Description

saveTherapyDetails() set _saveTherapyStatus = SaveTherapyStatus.loading but never called notifyListeners(), so the save button stayed fully active during the network call with no visual feedback. On any slow connection a therapist could tap the button multiple times, inserting duplicate rows into the therapy_goal table. The patient Reports screen then appended regressions from every row with no deduplication, causing the same regression to appear multiple times and making the patient's clinical profile look worse than reality.

🔧 Changes Made

  • Added a re-entry guard to saveTherapyDetails() that returns early if status is already loading, preventing concurrent duplicate submissions
  • Called notifyListeners() immediately after setting loading state so the UI reacts
  • Changed return type from void to Future<void>
  • Wrapped the method body in try-catch-finally so notifyListeners() fires on all paths including exceptions
  • Added isLoading parameter to SaveTherapyButton - sets onTap to null and shows a CircularProgressIndicator when loading
  • Added Opacity wrapper to visually indicate disabled state during submission
  • Passed therapyProvider.saveTherapyStatus.isLoading to the button in therapy_goals_screen.dart
  • Added Set<String> seenRegressions deduplication in getReports() so duplicate therapy_goal rows in the database do not inflate the patient's regression report

📷 Screenshots or Visual Changes (if applicable)

Not applicable. The core fix is in data integrity and provider state. The only visual change is the save button now shows a spinner and dims during submission.

🤝 Collaboration

Collaborated with: NONE

✅ Checklist

  • I have read the contributing guidelines.
  • I have added tests that prove my fix is effective or that my feature works.
  • I have added necessary documentation (if applicable).
  • Any dependent changes have been merged and published in downstream modules.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed duplicate regression names appearing in patient reports, ensuring each regression is listed only once for improved clarity.
    • Improved error handling and state management when saving therapy details to ensure application consistency and reliability.
  • New Features

    • Added visual loading indicator to the therapy goals save button, providing clear feedback during save operations.

Copilot AI review requested due to automatic review settings April 19, 2026 11:46
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 19, 2026

📝 Walkthrough

Walkthrough

These changes implement a three-part fix for the therapy details save button to prevent duplicate submissions and data corruption: adding a concurrent-save guard in the provider, updating the button to display loading state and disable interaction during saves, and deduplicating regression names in reports.

Changes

Cohort / File(s) Summary
Therapy Provider State Management
therapist/lib/provider/therapy_provider.dart
Refactored saveTherapyDetails to return Future<void>, added early-exit guard when _saveTherapyStatus.isLoading is true, called notifyListeners() immediately after setting loading state, and wrapped body in try/catch/finally to guarantee state updates on all paths (success, failure, exception).
Save Button Loading State
therapist/lib/presentation/therapy_goals/widgets/save_therapy_button.dart
Added optional isLoading parameter (defaults to false), made onPressed nullable, disabled tap handling when loading, and rendered CircularProgressIndicator with dimmed opacity during load instead of button text.
Therapy Goals Screen Integration
therapist/lib/presentation/therapy_goals/therapy_goals_screen.dart
Wired isLoading prop from therapyProvider.saveTherapyStatus.isLoading to SaveTherapyButton.
Report Deduplication
patient/lib/repository/supabase_patient_repository.dart
Added seenRegressions set in getReports to deduplicate regression names during report aggregation.

Sequence Diagram

sequenceDiagram
    participant User
    participant SaveTherapyButton as Save Button
    participant TherapyProvider as TherapyProvider
    participant SupabaseRepo as SupabaseRepository
    participant Supabase

    User->>SaveTherapyButton: Tap "Save Therapy Details"
    activate SaveTherapyButton
    SaveTherapyButton->>TherapyProvider: saveTherapyDetails()
    activate TherapyProvider
    
    Note over TherapyProvider: Check isLoading guard
    alt isLoading already true
        TherapyProvider-->>SaveTherapyButton: Return early (prevent duplicate)
    else isLoading false
        TherapyProvider->>TherapyProvider: Set status = loading
        TherapyProvider->>SaveTherapyButton: notifyListeners() → UI updates
        SaveTherapyButton->>SaveTherapyButton: Show CircularProgressIndicator
        SaveTherapyButton->>SaveTherapyButton: Disable onTap (null)
        
        TherapyProvider->>SupabaseRepo: saveTherapyGoals(...)
        activate SupabaseRepo
        SupabaseRepo->>Supabase: Insert therapy_goal (once, no duplicates)
        Supabase-->>SupabaseRepo: ActionResult
        deactivate SupabaseRepo
        
        alt Success
            TherapyProvider->>TherapyProvider: Set status = success
        else Error
            TherapyProvider->>TherapyProvider: Set status = failure
        end
        
        TherapyProvider->>SaveTherapyButton: notifyListeners() (in finally)
        SaveTherapyButton->>SaveTherapyButton: Hide loading indicator
        SaveTherapyButton->>SaveTherapyButton: Re-enable button
    end
    
    deactivate TherapyProvider
    deactivate SaveTherapyButton
    
    Note over Supabase: getReports deduplicates<br/>regression names via Set
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly Related PRs

  • Khanjasir90/therapist dashboard #114: Both PRs modify therapist/lib/provider/therapy_provider.dart in the saveTherapyDetails method; this PR refactors async/loading/status handling while the retrieved PR manages patientId assignment within the same flow.
  • feat: patient reports screen #121: Both PRs modify SupabasePatientRepository.getReports; this PR adds deduplication that directly protects data populated by the regression-list logic in the retrieved PR.

Poem

🐰 A button that bounces won't bounce anymore,
With guards and with loading it blocks the encore,
And regression reports, no longer inflate,
When duplicates deduplicate—oh what a fate! 🌿✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the three main changes: adding a submission guard, loading state to therapy save, and deduplication of regression reports.
Linked Issues check ✅ Passed The PR implements all coding requirements from issue #221: re-entry guard, immediate notifyListeners(), Future return type, try-catch-finally, isLoading parameter in SaveTherapyButton, provider state consumption, and Set-based deduplication in getReports().
Out of Scope Changes check ✅ Passed All changes directly address the requirements in issue #221; no unrelated modifications detected. The four modified files correspond exactly to the specified fix targets.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
therapist/lib/presentation/therapy_goals/therapy_goals_screen.dart (1)

111-119: ⚠️ Potential issue | 🔴 Critical

Declare the provider before reading its loading state.

Line 118 references therapyProvider.saveTherapyStatus.isLoading, but the variable is never declared. Line 111 reads Provider.of<TherapyProvider>(context, listen: true).saveTherapyStatus and discards the result instead of assigning it. This introduces an undefined identifier and blocks compilation.

Proposed fix
-    Provider.of<TherapyProvider>(context, listen: true).saveTherapyStatus;
+    final therapyProvider = Provider.of<TherapyProvider>(context, listen: true);
     return Scaffold(
       appBar: _getAppBar(),
       bottomNavigationBar: Padding(
         padding: const EdgeInsets.symmetric(horizontal: 22, vertical: 25),
         child: SaveTherapyButton(
           text: 'Save Therapy Details',
           isLoading: therapyProvider.saveTherapyStatus.isLoading,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@therapist/lib/presentation/therapy_goals/therapy_goals_screen.dart` around
lines 111 - 119, The code is discarding the provider result and then uses an
undeclared therapyProvider variable; replace the stray expression by declaring a
local variable (e.g., final therapyProvider =
Provider.of<TherapyProvider>(context, listen: true)) before returning the
Scaffold so SaveTherapyButton can read
therapyProvider.saveTherapyStatus.isLoading, and remove the unused
Provider.of(...) expression; ensure _onSaveTherapyDetails and SaveTherapyButton
remain unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@therapist/lib/presentation/therapy_goals/therapy_goals_screen.dart`:
- Around line 111-119: The code is discarding the provider result and then uses
an undeclared therapyProvider variable; replace the stray expression by
declaring a local variable (e.g., final therapyProvider =
Provider.of<TherapyProvider>(context, listen: true)) before returning the
Scaffold so SaveTherapyButton can read
therapyProvider.saveTherapyStatus.isLoading, and remove the unused
Provider.of(...) expression; ensure _onSaveTherapyDetails and SaveTherapyButton
remain unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 19440b3e-4343-4c27-9615-097243c437a6

📥 Commits

Reviewing files that changed from the base of the PR and between 051a4f3 and 6d874f9.

📒 Files selected for processing (4)
  • patient/lib/repository/supabase_patient_repository.dart
  • therapist/lib/presentation/therapy_goals/therapy_goals_screen.dart
  • therapist/lib/presentation/therapy_goals/widgets/save_therapy_button.dart
  • therapist/lib/provider/therapy_provider.dart

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR addresses duplicate therapy-goal submissions caused by missing loading-state UI updates and adds defensive deduplication of regressions in patient reports to prevent inflated regression lists when duplicate DB rows exist.

Changes:

  • Add a loading re-entry guard + immediate notifyListeners() in TherapyProvider.saveTherapyDetails() with try/catch/finally.
  • Add isLoading UI state to SaveTherapyButton (disable + spinner + dim).
  • Deduplicate regression names in patient getReports() results via a Set.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

File Description
therapist/lib/provider/therapy_provider.dart Adds loading guard, ensures listeners are notified for loading/success/failure, and updates method to Future<void>.
therapist/lib/presentation/therapy_goals/widgets/save_therapy_button.dart Adds loading UI/disabled behavior to the save button.
therapist/lib/presentation/therapy_goals/therapy_goals_screen.dart Wires provider loading state into the save button.
patient/lib/repository/supabase_patient_repository.dart Deduplicates regressions collected from multiple therapy_goal rows.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 242 to 244
List<String> regressions = [];
for(var i = 0; i < regresstionResponse.length; i++) {
final regression = regresstionResponse[i]['regressions'];
Comment on lines 116 to 120
child: SaveTherapyButton(
text: 'Save Therapy Details',
isLoading: therapyProvider.saveTherapyStatus.isLoading,
onPressed: _onSaveTherapyDetails,
),
final String text;
final VoidCallback onPressed;

final VoidCallback? onPressed;
Comment on lines +20 to +46
child: Opacity(
opacity: isLoading ? 0.6 : 1.0,
child: Container(
width: double.infinity,
height: 50,
decoration: BoxDecoration(
color: AppTheme.primaryColor,
borderRadius: BorderRadius.circular(10),
),
child: Center(
child: isLoading
? const SizedBox(
height: 24,
width: 24,
child: CircularProgressIndicator(
color: Colors.white,
strokeWidth: 2.5,
),
)
: Text(
text,
style: const TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.w700,
),
),
Comment on lines +244 to +247
} else {
_saveTherapyStatus = SaveTherapyStatus.failure;
_saveTherapyErrorMessage = result.errorMessage.toString();
}
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.

BUG: Therapy details save button has no loading guard, allowing duplicate submissions that inflate patient regression reports

2 participants