Skip to content

Commit bba5de9

Browse files
authored
Merge pull request #433 from dwarwick/copilot/add-question-group-branching
Implement survey branching with question groups and conditional logic
2 parents cf26c35 + 38148a3 commit bba5de9

29 files changed

+3394
-93
lines changed

.github/copilot-instructions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,4 @@ When using Mudchip, do not add a Closable attribute. To make it closable, just d
2525
When creating a new Razor component, always create a code behind file with the same name as the Razor component file, plus Model. For example, if the Razor component file is named MyComponent.razor, the code behind file should be named MyComponentModel.razor.cs.
2626

2727
Do not create a PR unless all tests pass.
28-
AGENTS.md files in the solution also contain copilot instructions for specific projects.
28+
AGENTS.md files in the solution also contain copilot instructions for specific projects.29.MudBlazor MudCheckBox API: Use Value and ValueChanged properties (NOT Checked/CheckedChanged which are deprecated). Example: `<MudCheckBox T="bool" Value="@myValue" ValueChanged="@((bool val) => HandleChange(val))" />`

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,4 @@ app.css is used for styles that are common to both dark mode and light mode. Do
2929
Playwright end-to-end tests live in the JwtIdentity.PlaywrightTests project and use NUnit. Install browsers with `pwsh bin/Debug/net9.0/playwright.ps1 install` after the first build, and run the suite with `dotnet test JwtIdentity.PlaywrightTests`.
3030
The projects in the solution explicitly disable nullable reference types. Do not enable nullable reference types in any of the projects. If you find nullable reference types enabled in any of the projects, please disable them. If you find that any reference types are marked as nullable, please remove the nullable annotation.
3131
The MudStack Wrap attribute expects a Wrap enum value. See DemoLanding.razor for an example.
32-
When you build the solution, check for new warnings and fix them. Do not ignore new warnings. Warnings should be treated as errors and fixed before committing code.
32+
When you build the solution, check for new warnings and fix them. Do not ignore new warnings. Warnings should be treated as errors and fixed before committing code.33.MudBlazor MudCheckBox API: Use Value and ValueChanged properties (NOT Checked/CheckedChanged which are deprecated). Example: `<MudCheckBox T="bool" Value="@myValue" ValueChanged="@((bool val) => HandleChange(val))" />`

BRANCHING_IMPLEMENTATION_STATUS.md

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
# Survey Branching Implementation Status
2+
3+
## Completed Features ✅
4+
5+
### Database Layer
6+
- ✅ Added `QuestionGroup` model with properties:
7+
- Id, SurveyId, GroupNumber, GroupName, NextGroupId, SubmitAfterGroup
8+
- ✅ Added `GroupId` property to `Question` model (default 0)
9+
- ✅ Added `BranchToGroupId` property to `ChoiceOption` model for branching logic
10+
- ✅ Created and applied migration `20251013180811_AddSurveyBranching`
11+
12+
### ViewModels
13+
- ✅ Created `QuestionGroupViewModel` in Common project
14+
- ✅ Updated `ChoiceOptionViewModel` with `BranchToGroupId`
15+
- ✅ Updated `QuestionViewModel` with `GroupId`
16+
- ✅ Updated `SurveyViewModel` with `QuestionGroups` list
17+
- ✅ Updated AutoMapper configuration for all new mappings
18+
19+
### API Layer
20+
- ✅ Created `QuestionGroupController` with full CRUD operations:
21+
- GET api/QuestionGroup/Survey/{surveyId} - Get groups for a survey
22+
- GET api/QuestionGroup/{id} - Get specific group
23+
- POST api/QuestionGroup - Create new group
24+
- PUT api/QuestionGroup - Update existing group
25+
- DELETE api/QuestionGroup/{id} - Delete group (moves questions to group 0)
26+
- ✅ Added UpdateGroup endpoint to `QuestionController`
27+
- ✅ Updated `SurveyController` to include QuestionGroups when fetching surveys
28+
- ✅ Added `QuestionGroup` constant to `ApiEndpoints`
29+
30+
### UI - Survey Creation/Management
31+
- ✅ Created `BranchingSurveyEdit.razor` page with comprehensive UI:
32+
- Collapsible accordion panels for each question group
33+
- Add/delete question groups
34+
- Assign custom names to groups
35+
- Move questions between groups
36+
- Configure branching for Multiple Choice, Select All That Apply, and True/False questions
37+
- Set group completion behavior (Submit or Branch to another group)
38+
- Visual indicators showing group names or numbers
39+
- ✅ Added "Configure Branching" button to main EditSurvey page
40+
- ✅ Full drag-and-drop support maintained for question ordering
41+
- ✅ Validation to ensure deleted groups move questions back to group 0
42+
43+
## Remaining Work 🚧
44+
45+
### UI - Survey Taking Experience
46+
The following enhancements are needed in `Survey.razor` and `Survey.razor.cs`:
47+
48+
#### 1. One-Question-at-a-Time Display
49+
When a survey has multiple groups (more than just group 0), the interface should:
50+
- Display only the current question instead of all questions
51+
- Show Previous/Next buttons for navigation
52+
- Show Submit button only at the end of the survey
53+
- Disable Next button until current question is answered
54+
55+
#### 2. Progress Indicator
56+
- Add a progress bar or counter showing "Question X of Y"
57+
- Update dynamically as user progresses through branching paths
58+
- Account for conditional questions that may or may not be shown
59+
60+
#### 3. Branching Logic Implementation
61+
In `Survey.razor.cs`, implement:
62+
63+
```csharp
64+
// Track which questions have been shown in current session
65+
private List<int> _shownQuestions = new();
66+
private int _currentQuestionIndex = 0;
67+
68+
// Determine next question based on answer and branching rules
69+
private int? GetNextQuestion(QuestionViewModel currentQuestion, AnswerViewModel answer)
70+
{
71+
// For Multiple Choice - check if selected option has branching
72+
if (answer is MultipleChoiceAnswerViewModel mcAnswer)
73+
{
74+
var option = GetSelectedOption(currentQuestion, mcAnswer.SelectedOptionId);
75+
if (option?.BranchToGroupId != null)
76+
{
77+
return GetFirstQuestionInGroup(option.BranchToGroupId.Value);
78+
}
79+
}
80+
81+
// For Select All That Apply - determine branching based on checked options
82+
// (Implementation note: may need to decide on priority if multiple options selected)
83+
84+
// For True/False - check branching configuration
85+
// (Note: Need to extend ChoiceOption or add separate table for True/False branching)
86+
87+
// Check if current group is complete
88+
var currentGroup = GetQuestionGroup(currentQuestion.GroupId);
89+
if (IsGroupComplete(currentGroup))
90+
{
91+
if (currentGroup.SubmitAfterGroup)
92+
{
93+
return null; // End survey
94+
}
95+
else if (currentGroup.NextGroupId.HasValue)
96+
{
97+
return GetFirstQuestionInGroup(GetGroupNumber(currentGroup.NextGroupId.Value));
98+
}
99+
}
100+
101+
// Default: return next question in sequence
102+
return GetNextQuestionInSequence(currentQuestion);
103+
}
104+
```
105+
106+
#### 4. Captcha Handling
107+
- Ensure captcha is shown at the START of survey (already implemented)
108+
- Do not show captcha again during branching navigation
109+
110+
#### 5. Answer Persistence
111+
- Save answers immediately when user moves to next question
112+
- Handle case where user goes back and changes an answer
113+
- Recalculate branching path if answers change
114+
115+
### Data Model Considerations
116+
117+
#### True/False Branching
118+
Currently, True/False questions don't have "options" like Multiple Choice. Consider:
119+
- **Option A**: Extend True/False to have two implicit ChoiceOptions (True, False)
120+
- **Option B**: Add separate branching fields to TrueFalseQuestion model
121+
- **Current Implementation**: Uses Dictionary in UI (TrueBranch/FalseBranch) but not persisted
122+
123+
**Recommendation**: Add BranchToGroupIdOnTrue and BranchToGroupIdOnFalse to TrueFalseQuestion model.
124+
125+
### Testing Requirements
126+
127+
1. **Single Group Survey** - Ensure existing surveys (all in group 0) continue to work exactly as before
128+
2. **Linear Branching** - Group 0 → Group 1 → Group 2 → Submit
129+
3. **Conditional Branching** - Multiple choice option determines which group to show next
130+
4. **Skip Groups** - Group 0 → Group 3 (skip 1 and 2 based on answer)
131+
5. **Return to Earlier Group** - Group 0 → Group 1 → back to Group 0 questions
132+
6. **Dead End Detection** - Validate that all branches eventually lead to submission
133+
7. **Select All That Apply** - Handle branching when multiple options selected
134+
135+
### Documentation Updates
136+
137+
- Update CreatingSurveys.razor docs page with branching instructions
138+
- Add screenshots of branching UI
139+
- Create user guide for setting up conditional logic
140+
- Document best practices for survey flow design
141+
142+
## Known Limitations
143+
144+
1. **Select All That Apply Branching**: When multiple options are selected, need to decide on branch priority
145+
- Possible solutions: Take first selected option's branch, OR show union of all branched groups
146+
147+
2. **True/False Branching Persistence**: Currently only configured in UI, needs backend persistence
148+
149+
3. **Circular References**: No validation yet to prevent Group A → Group B → Group A loops
150+
151+
4. **Group Deletion**: When deleting a group that is referenced as NextGroupId by another group,
152+
need to update those references
153+
154+
## Architecture Decisions
155+
156+
### Why Separate Branching Page?
157+
- EditSurvey.razor was already feeling crowded (per requirements)
158+
- Branching is advanced functionality - not needed for simple surveys
159+
- Separates concerns: question content vs. flow logic
160+
- Easier to navigate and understand for users
161+
162+
### Why Group Numbers vs. IDs?
163+
- GroupNumber (0, 1, 2...) is more user-friendly than database IDs
164+
- Allows easy reference in UI ("Go to Group 2")
165+
- Group 0 is special "default" group that always exists
166+
- Actual database uses Ids for relationships, GroupNumber for display
167+
168+
### Why Not Question-Level Next?
169+
- Group-based branching is clearer for users to understand and configure
170+
- Reduces complexity of the branching UI
171+
- Still allows fine-grained control by putting each question in its own group if needed
172+
- More maintainable codebase
173+
174+
## Next Steps
175+
176+
Priority order for remaining implementation:
177+
178+
1. **High Priority**: Implement one-question-at-a-time display when groups exist
179+
2. **High Priority**: Implement basic branching logic in Survey.razor.cs
180+
3. **Medium Priority**: Add progress indicator
181+
4. **Medium Priority**: Handle True/False branching persistence
182+
5. **Low Priority**: Add circular reference validation
183+
6. **Low Priority**: Update documentation
184+
185+
## Migration Path for Existing Surveys
186+
187+
All existing surveys will automatically have their questions in Group 0 (default value).
188+
They will continue to display all questions on one page unless groups are explicitly added.
189+
This ensures backward compatibility.

0 commit comments

Comments
 (0)