docs: create milestone v2.3 roadmap (5 phases, 15-19)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dev
2026-04-09 11:14:03 +02:00
parent d967a8bb65
commit e3ff27a673
60 changed files with 1138 additions and 35 deletions

View File

@@ -0,0 +1,275 @@
---
phase: 14-user-directory-view
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- SharepointToolbox/Localization/Strings.resx
- SharepointToolbox/Localization/Strings.fr.resx
- SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs
- SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml.cs
- SharepointToolbox.Tests/ViewModels/UserAccessAuditViewModelDirectoryTests.cs
autonomous: true
requirements:
- UDIR-05
- UDIR-01
must_haves:
truths:
- "SelectDirectoryUserCommand takes a GraphDirectoryUser, converts it to GraphUserResult, adds it to SelectedUsers via existing logic"
- "After SelectDirectoryUserCommand, the user appears in SelectedUsers and can be audited with RunCommand"
- "SelectDirectoryUserCommand does not add duplicates (same UPN check as existing AddUserCommand)"
- "Localization keys for directory UI exist in both EN and FR resource files"
- "Code-behind has a DirectoryDataGrid_MouseDoubleClick handler that invokes SelectDirectoryUserCommand"
artifacts:
- path: "SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs"
provides: "SelectDirectoryUserCommand bridging directory selection to audit pipeline"
contains: "SelectDirectoryUserCommand"
- path: "SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml.cs"
provides: "Event handler for directory DataGrid double-click"
contains: "DirectoryDataGrid_MouseDoubleClick"
- path: "SharepointToolbox.Tests/ViewModels/UserAccessAuditViewModelDirectoryTests.cs"
provides: "Tests for SelectDirectoryUserCommand"
contains: "SelectDirectoryUser"
key_links:
- from: "SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs"
to: "SharepointToolbox/Core/Models/GraphDirectoryUser.cs"
via: "command parameter type"
pattern: "GraphDirectoryUser"
- from: "SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml.cs"
to: "SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs"
via: "command invocation"
pattern: "SelectDirectoryUserCommand"
---
<objective>
Add localization keys for directory UI, the SelectDirectoryUserCommand that bridges directory selection to the audit pipeline, and a code-behind event handler for DataGrid double-click.
Purpose: Provides the infrastructure (localization, command, event handler) that Plan 14-02 needs to build the XAML view. SC2 requires selecting a directory user to trigger an audit — this command makes that possible.
Output: Localization keys (EN+FR), SelectDirectoryUserCommand with tests, code-behind event handler.
</objective>
<execution_context>
@C:/Users/dev/.claude/get-shit-done/workflows/execute-plan.md
@C:/Users/dev/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/phases/14-user-directory-view/14-RESEARCH.md
<interfaces>
<!-- Current ViewModel command pattern (AddUserCommand) -->
From SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs:
```csharp
public RelayCommand<GraphUserResult> AddUserCommand { get; }
private void ExecuteAddUser(GraphUserResult? user)
{
if (user == null) return;
if (!SelectedUsers.Any(u => u.UserPrincipalName == user.UserPrincipalName))
{
SelectedUsers.Add(user);
}
SearchQuery = string.Empty;
SearchResults.Clear();
}
```
<!-- GraphDirectoryUser record -->
From SharepointToolbox/Core/Models/GraphDirectoryUser.cs:
```csharp
public record GraphDirectoryUser(
string DisplayName, string UserPrincipalName,
string? Mail, string? Department, string? JobTitle, string? UserType);
```
<!-- GraphUserResult record -->
From SharepointToolbox/Services/IGraphUserSearchService.cs:
```csharp
public record GraphUserResult(string DisplayName, string UserPrincipalName, string? Mail);
```
<!-- Existing code-behind pattern -->
From SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml.cs:
```csharp
private void SearchResultsListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (sender is ListBox listBox && listBox.SelectedItem is GraphUserResult user)
{
var vm = (UserAccessAuditViewModel)DataContext;
if (vm.AddUserCommand.CanExecute(user))
vm.AddUserCommand.Execute(user);
listBox.SelectedItem = null;
}
}
```
<!-- Existing localization key pattern -->
From Strings.resx:
```xml
<data name="audit.grp.users" xml:space="preserve"><value>Select Users</value></data>
<data name="audit.btn.run" xml:space="preserve"><value>Run Audit</value></data>
```
</interfaces>
</context>
<tasks>
<task type="auto">
<name>Task 1: Add localization keys for directory UI (EN + FR)</name>
<files>
SharepointToolbox/Localization/Strings.resx,
SharepointToolbox/Localization/Strings.fr.resx
</files>
<behavior>
- Both resx files contain matching keys for directory browse UI
</behavior>
<action>
1. Add to `Strings.resx` (EN):
- `audit.mode.search` = "Search"
- `audit.mode.browse` = "Browse Directory"
- `directory.grp.browse` = "User Directory"
- `directory.btn.load` = "Load Directory"
- `directory.btn.cancel` = "Cancel"
- `directory.filter.placeholder` = "Filter users..."
- `directory.chk.guests` = "Include guests"
- `directory.status.count` = "users"
- `directory.hint.doubleclick` = "Double-click a user to add to audit"
- `directory.col.name` = "Name"
- `directory.col.upn` = "Email"
- `directory.col.department` = "Department"
- `directory.col.jobtitle` = "Job Title"
- `directory.col.type` = "Type"
2. Add to `Strings.fr.resx` (FR):
- `audit.mode.search` = "Recherche"
- `audit.mode.browse` = "Parcourir l'annuaire"
- `directory.grp.browse` = "Annuaire utilisateurs"
- `directory.btn.load` = "Charger l'annuaire"
- `directory.btn.cancel` = "Annuler"
- `directory.filter.placeholder` = "Filtrer les utilisateurs..."
- `directory.chk.guests` = "Inclure les invités"
- `directory.status.count` = "utilisateurs"
- `directory.hint.doubleclick` = "Double-cliquez sur un utilisateur pour l'ajouter à l'audit"
- `directory.col.name` = "Nom"
- `directory.col.upn` = "Courriel"
- `directory.col.department` = "Département"
- `directory.col.jobtitle` = "Poste"
- `directory.col.type` = "Type"
</action>
<verify>
<automated>dotnet build --no-restore -warnaserror</automated>
</verify>
<done>14 localization keys present in both EN and FR resource files.</done>
</task>
<task type="auto" tdd="true">
<name>Task 2: Add SelectDirectoryUserCommand to ViewModel</name>
<files>
SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs,
SharepointToolbox.Tests/ViewModels/UserAccessAuditViewModelDirectoryTests.cs
</files>
<behavior>
- SelectDirectoryUserCommand is a RelayCommand<GraphDirectoryUser>
- It converts GraphDirectoryUser to GraphUserResult and adds to SelectedUsers
- Duplicate UPN check (same as AddUserCommand)
- Does NOT clear SearchQuery/SearchResults (not in search mode context)
- After execution, IsBrowseMode stays true — user can continue selecting from directory
</behavior>
<action>
1. Add command declaration in ViewModel:
```csharp
public RelayCommand<GraphDirectoryUser> SelectDirectoryUserCommand { get; }
```
2. Initialize in BOTH constructors:
```csharp
SelectDirectoryUserCommand = new RelayCommand<GraphDirectoryUser>(ExecuteSelectDirectoryUser);
```
3. Implement the command method:
```csharp
private void ExecuteSelectDirectoryUser(GraphDirectoryUser? dirUser)
{
if (dirUser == null) return;
var userResult = new GraphUserResult(dirUser.DisplayName, dirUser.UserPrincipalName, dirUser.Mail);
if (!SelectedUsers.Any(u => u.UserPrincipalName == userResult.UserPrincipalName))
{
SelectedUsers.Add(userResult);
}
}
```
4. Add tests to `UserAccessAuditViewModelDirectoryTests.cs`:
- Test: SelectDirectoryUserCommand adds user to SelectedUsers
- Test: SelectDirectoryUserCommand skips duplicates
- Test: SelectDirectoryUserCommand with null does nothing
- Test: After SelectDirectoryUser, user can be audited with RunCommand (integration: add user + check SelectedUsers.Count > 0)
</action>
<verify>
<automated>dotnet build --no-restore -warnaserror && dotnet test SharepointToolbox.Tests --filter "FullyQualifiedName~UserAccessAuditViewModelDirectory" --no-build -q</automated>
</verify>
<done>SelectDirectoryUserCommand bridges directory selection to audit pipeline. Tests pass.</done>
</task>
<task type="auto">
<name>Task 3: Add code-behind event handler for directory DataGrid</name>
<files>
SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml.cs
</files>
<behavior>
- DirectoryDataGrid_MouseDoubleClick handler extracts the clicked GraphDirectoryUser
- Invokes SelectDirectoryUserCommand with the selected user
- Uses the same pattern as SearchResultsListBox_SelectionChanged
</behavior>
<action>
1. Add to `UserAccessAuditView.xaml.cs`:
```csharp
private void DirectoryDataGrid_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (sender is DataGrid grid && grid.SelectedItem is GraphDirectoryUser user)
{
var vm = (UserAccessAuditViewModel)DataContext;
if (vm.SelectDirectoryUserCommand.CanExecute(user))
vm.SelectDirectoryUserCommand.Execute(user);
}
}
```
2. Add the required using statement if not present:
```csharp
using System.Windows.Controls; // Already present
using SharepointToolbox.Core.Models; // For GraphDirectoryUser
```
</action>
<verify>
<automated>dotnet build --no-restore -warnaserror</automated>
</verify>
<done>Code-behind event handler exists, ready to be wired in XAML (Plan 14-02).</done>
</task>
</tasks>
<verification>
```bash
dotnet build --no-restore -warnaserror
dotnet test SharepointToolbox.Tests --filter "FullyQualifiedName~UserAccessAuditViewModelDirectory" --no-build -q
```
Both must pass with zero failures.
</verification>
<success_criteria>
- 14 localization keys in both EN and FR resx files
- SelectDirectoryUserCommand converts GraphDirectoryUser → GraphUserResult → SelectedUsers
- Duplicate UPN check prevents adding same user twice
- Code-behind event handler for DataGrid double-click
- All tests pass, build clean
</success_criteria>
<output>
After completion, create `.planning/phases/14-user-directory-view/14-01-SUMMARY.md`
</output>