fix: add submission guard and loading state to therapy save, deduplicate regression reports#222
Conversation
…ate regression reports
📝 WalkthroughWalkthroughThese 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
Sequence DiagramsequenceDiagram
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
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly Related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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 | 🔴 CriticalDeclare the provider before reading its loading state.
Line 118 references
therapyProvider.saveTherapyStatus.isLoading, but the variable is never declared. Line 111 readsProvider.of<TherapyProvider>(context, listen: true).saveTherapyStatusand 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
📒 Files selected for processing (4)
patient/lib/repository/supabase_patient_repository.darttherapist/lib/presentation/therapy_goals/therapy_goals_screen.darttherapist/lib/presentation/therapy_goals/widgets/save_therapy_button.darttherapist/lib/provider/therapy_provider.dart
There was a problem hiding this comment.
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()inTherapyProvider.saveTherapyDetails()withtry/catch/finally. - Add
isLoadingUI state toSaveTherapyButton(disable + spinner + dim). - Deduplicate regression names in patient
getReports()results via aSet.
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.
| List<String> regressions = []; | ||
| for(var i = 0; i < regresstionResponse.length; i++) { | ||
| final regression = regresstionResponse[i]['regressions']; |
| child: SaveTherapyButton( | ||
| text: 'Save Therapy Details', | ||
| isLoading: therapyProvider.saveTherapyStatus.isLoading, | ||
| onPressed: _onSaveTherapyDetails, | ||
| ), |
| final String text; | ||
| final VoidCallback onPressed; | ||
|
|
||
| final VoidCallback? onPressed; |
| 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, | ||
| ), | ||
| ), |
| } else { | ||
| _saveTherapyStatus = SaveTherapyStatus.failure; | ||
| _saveTherapyErrorMessage = result.errorMessage.toString(); | ||
| } |
Closes #221
📝 Description
saveTherapyDetails()set_saveTherapyStatus = SaveTherapyStatus.loadingbut never callednotifyListeners(), 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 thetherapy_goaltable. 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
saveTherapyDetails()that returns early if status is already loading, preventing concurrent duplicate submissionsnotifyListeners()immediately after setting loading state so the UI reactsvoidtoFuture<void>notifyListeners()fires on all paths including exceptionsisLoadingparameter toSaveTherapyButton- setsonTapto null and shows aCircularProgressIndicatorwhen loadingOpacitywrapper to visually indicate disabled state during submissiontherapyProvider.saveTherapyStatus.isLoadingto the button intherapy_goals_screen.dartSet<String> seenRegressionsdeduplication ingetReports()so duplicatetherapy_goalrows 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
Summary by CodeRabbit
Bug Fixes
New Features