|
| 1 | +═══════════════════════════════════════════════════════════════════════════════════════ |
| 2 | + btnDisabled$ BehaviorSubject Flow Diagram |
| 3 | +═══════════════════════════════════════════════════════════════════════════════════════ |
| 4 | + |
| 5 | +┌─────────────────────────────────────┐ |
| 6 | +│ activity-desktop.page.ts │ |
| 7 | +│ (Parent Component) │ |
| 8 | +└─────────────────────────────────────┘ |
| 9 | + │ |
| 10 | + │ Creates & Passes btnDisabled$ |
| 11 | + │ as @Input to assessment.component |
| 12 | + ▼ |
| 13 | +┌─────────────────────────────────────┐ |
| 14 | +│ assessment.component.ts │ |
| 15 | +│ (Child Component) │ |
| 16 | +│ │ |
| 17 | +│ @Input() btnDisabled$: │ |
| 18 | +│ BehaviorSubject<boolean> │ |
| 19 | +└─────────────────────────────────────┘ |
| 20 | + |
| 21 | +═══════════════════════════════════════════════════════════════════════════════════════ |
| 22 | + TRIGGER POINTS IN assessment.component.ts |
| 23 | +═══════════════════════════════════════════════════════════════════════════════════════ |
| 24 | + |
| 25 | +1. ngOnChanges() - Component Lifecycle |
| 26 | + └── btnDisabled$.next(false) ──────────► RESET on assessment change |
| 27 | + |
| 28 | +2. _populateQuestionsForm() - Form Setup |
| 29 | + ├── If no questions exist: |
| 30 | + │ └── btnDisabled$.next(true) ───────► DISABLE (empty form) |
| 31 | + │ |
| 32 | + └── questionsForm.valueChanges.subscribe() |
| 33 | + └── setSubmissionDisabled() ───────► CHECK & UPDATE based on validation |
| 34 | + |
| 35 | +3. _handleSubmissionData() - Submission State Handler |
| 36 | + └── If submission.isLocked: |
| 37 | + └── btnDisabled$.next(true) ───────► DISABLE (locked by another user) |
| 38 | + |
| 39 | +4. _handleReviewData() - Review State Handler |
| 40 | + └── If isPendingReview && review.status === 'in progress': |
| 41 | + └── btnDisabled$.next(false) ──────► ENABLE for review |
| 42 | + |
| 43 | +5. continueToNextTask() - Submit Action |
| 44 | + └── If _btnAction === 'submit': |
| 45 | + └── btnDisabled$.next(true) ───────► DISABLE during submission |
| 46 | + |
| 47 | +6. _submitAnswer() - Answer Submission |
| 48 | + └── If required questions missing: |
| 49 | + └── btnDisabled$.next(false) ──────► RE-ENABLE after validation fail |
| 50 | + |
| 51 | +7. resubmit() - Resubmission Flow |
| 52 | + ├── Start: btnDisabled$.next(true) ────► DISABLE during resubmit |
| 53 | + └── End: btnDisabled$.next(false) ─────► RE-ENABLE after completion |
| 54 | + |
| 55 | +8. setSubmissionDisabled() - Main Validation Logic |
| 56 | + ├── Only runs if (doAssessment || isPendingReview) |
| 57 | + ├── If form invalid & not disabled: |
| 58 | + │ └── btnDisabled$.next(true) ───────► DISABLE |
| 59 | + └── If form valid & disabled: |
| 60 | + └── btnDisabled$.next(false) ──────► ENABLE |
| 61 | + |
| 62 | +9. _prefillForm() - Form Population |
| 63 | + ├── After populating form with answers |
| 64 | + ├── questionsForm.updateValueAndValidity() |
| 65 | + └── If edit mode (doAssessment || isPendingReview): |
| 66 | + └── setSubmissionDisabled() ───────► CHECK & UPDATE validation |
| 67 | + └── If read-only mode: |
| 68 | + └── btnDisabled$.next(false) ──────► ENSURE enabled |
| 69 | + |
| 70 | +10. Page Navigation Methods |
| 71 | + ├── goToPage() |
| 72 | + ├── nextPage() |
| 73 | + └── prevPage() |
| 74 | + └── setSubmissionDisabled() ──────► CHECK & UPDATE for new page |
| 75 | + |
| 76 | +═══════════════════════════════════════════════════════════════════════════════════════ |
| 77 | + TRIGGER CONDITIONS SUMMARY |
| 78 | +═══════════════════════════════════════════════════════════════════════════════════════ |
| 79 | + |
| 80 | +DISABLE CONDITIONS (btnDisabled$.next(true)): |
| 81 | +├── No questions in assessment |
| 82 | +├── Assessment is locked by another user |
| 83 | +├── Form is invalid (required fields empty) |
| 84 | +├── During submission process |
| 85 | +└── During resubmit process |
| 86 | + |
| 87 | +ENABLE CONDITIONS (btnDisabled$.next(false)): |
| 88 | +├── Assessment changes (reset) |
| 89 | +├── Form becomes valid |
| 90 | +├── Review in progress |
| 91 | +├── After failed validation alert |
| 92 | +├── After resubmit completion |
| 93 | +└── Read-only mode (not doAssessment && not isPendingReview) |
| 94 | + |
| 95 | +═══════════════════════════════════════════════════════════════════════════════════════ |
| 96 | + PROBLEM SCENARIO |
| 97 | +═══════════════════════════════════════════════════════════════════════════════════════ |
| 98 | + |
| 99 | +User Flow - Original Issue (RESOLVED): |
| 100 | +1. User visits Assessment A (has required fields) |
| 101 | + └── Form invalid → btnDisabled$.next(true) ✓ |
| 102 | + |
| 103 | +2. User navigates to Assessment B via activity-desktop |
| 104 | + └── ngOnChanges() → btnDisabled$.next(false) ✓ |
| 105 | + └── _populateQuestionsForm() → questionsForm created |
| 106 | + └── _populateFormWithAnswers() → form populated |
| 107 | + └── setSubmissionDisabled() → checks validation |
| 108 | + └── BUT: Timing issue - form may not be fully populated |
| 109 | + └── Result: btnDisabled$ may remain false even if invalid |
| 110 | + |
| 111 | +3. RESOLVED: State synchronization fixed |
| 112 | + └── _prefillForm() now properly checks validation after form population |
| 113 | + |
| 114 | +═══════════════════════════════════════════════════════════════════════════════════════ |
| 115 | + SOLUTION IMPLEMENTATION (COMPLETED) |
| 116 | +═══════════════════════════════════════════════════════════════════════════════════════ |
| 117 | + |
| 118 | +IMPLEMENTED FIXES: |
| 119 | +1. ✅ Reset state in ngOnChanges when assessment changes |
| 120 | + └── btnDisabled$.next(false) in ngOnChanges() |
| 121 | + |
| 122 | +2. ✅ Proper validation after form population in _prefillForm() |
| 123 | + ├── questionsForm.updateValueAndValidity() |
| 124 | + ├── Edit mode: setSubmissionDisabled() checks validation |
| 125 | + └── Read-only mode: btnDisabled$.next(false) ensures enabled |
| 126 | + |
| 127 | +3. ✅ Check validation when changing pages |
| 128 | + ├── prevPage() → setSubmissionDisabled() |
| 129 | + ├── nextPage() → setSubmissionDisabled() |
| 130 | + └── goToPage() → setSubmissionDisabled() |
| 131 | + |
| 132 | +4. ✅ Apply validation rules only when in edit mode |
| 133 | + └── setSubmissionDisabled() has guard: (!doAssessment && !isPendingReview) |
| 134 | + |
| 135 | +5. ✅ Replaced _populateFormWithAnswers() with _prefillForm() |
| 136 | + └── Better state management and validation synchronization |
| 137 | + |
| 138 | +RESULT: btnDisabled$ now accurately reflects form state at all times |
| 139 | +═══════════════════════════════════════════════════════════════════════════════════════ |
| 140 | + |
| 141 | +═══════════════════════════════════════════════════════════════════════════════════════ |
| 142 | + KEY LEARNINGS |
| 143 | +═══════════════════════════════════════════════════════════════════════════════════════ |
| 144 | + |
| 145 | +1. BEHAVIORSUBJECT STATE PERSISTENCE |
| 146 | + └── BehaviorSubject remembers last value across component input changes |
| 147 | + └── Must explicitly reset when navigating between assessments |
| 148 | + |
| 149 | +2. FORM VALIDATION TIMING |
| 150 | + └── Validation must happen AFTER form population is complete |
| 151 | + └── updateValueAndValidity() is crucial for proper validation state |
| 152 | + |
| 153 | +3. SEPARATION OF CONCERNS |
| 154 | + └── setSubmissionDisabled() handles validation-based enabling/disabling |
| 155 | + └── _prefillForm() handles initial state setup after population |
| 156 | + └── Each method has clear responsibility boundaries |
| 157 | + |
| 158 | +4. EDIT VS READ-ONLY MODES |
| 159 | + └── Only apply validation rules when user can edit |
| 160 | + └── Read-only mode should always have enabled button for navigation |
| 161 | + └── Guard clauses prevent unnecessary state changes |
| 162 | + |
| 163 | +═══════════════════════════════════════════════════════════════════════════════════════ |
0 commit comments